簡體   English   中英

具有偏移量C#Linq的GroupBy DateTime列

[英]GroupBy DateTime column with offset C# Linq

在我的.NET 4.5.2 C#控制台應用程序中,我有一個名為someItemsList<SomeItem>其中SomeItem為:

public class SomeItem    {
    public int A { get; set; }
    public int B { get; set; }
    public DateTime C { get; set; }
}

然后我將這些項目分組到基於AB的新列表中:

var groupedItems = someItems.GroupBy(x => new { x.A, x.B });

我現在希望將C引入GroupBy ,但是有一個問題:我想將其C屬性SomeItems 2秒的( - 或者加或減)的SomeItems組合在一起(基本上是2秒的偏移量)。 因此,例如,兩個SomeItem ,一個C設置為2016-03-28 17:58:01.000 ,另一個設置為2016-03-28 17:58:03.000 ,將組合在一起。 那可能嗎?

編輯:對於所有意圖和目的,讓我們假設列表中沒有可能導致“重疊”組的項目(如評論中的@J.Sten的問題)

編輯2:一個例子

假設以下數據:

  • 15/06/2017 00:00:02
  • 15/06/2017 00:00:04
  • 15/06/2017 00:00:06
  • 15/06/2017 00:00:09
  • 15/06/2017 00:00:11
  • 15/06/2017 00:00:15

..並且偏移量為2秒,我希望它們按以下方式分組:

第1組:

  • 15/06/2017 00:00:02
  • 15/06/2017 00:00:04
  • 15/06/2017 00:00:06

第2組:

  • 15/06/2017 00:00:09
  • 15/06/2017 00:00:11

第3組:

  • 15/06/2017 00:00:15

您可以創建一個擴展方法來舍入秒數。 我找到了這個例子:讓datetime.now返回到最近的秒

這個問題的答案是:

public static DateTime Trim(this DateTime date, long ticks) {
   return new DateTime(date.Ticks - (date.Ticks % ticks), date.Kind);
}

然后,您可以通過以下方式在組中執行以下操作:

var result = dates.GroupBy(x => x.Trim(TimeSpan.FromSeconds(3).Ticks));

所以給出輸入

  • 15/06/2017 00:00:01
  • 15/06/2017 00:00:02
  • 15/06/2017 00:00:03
  • 15/06/2017 00:00:04
  • 15/06/2017 00:00:05
  • 15/06/2017 00:00:06

你會有3組:

A組

  1. 15/06/2017 00:00:01
  2. 15/06/2017 00:00:02

B組

  1. 15/06/2017 00:00:03
  2. 15/06/2017 00:00:04
  3. 15/06/2017 00:00:05

C組:

  1. 15/06/2017 00:00:06

下面的代碼應該可以解決問題。

基本上,它在多個傳遞中進行分組 - 首先在A和B上,然后使用TakeWhileSkip按照您希望對它們進行分組的方式對日期進行分組。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Test
{
    public class SomeItem
    {
        public int A { get; set; }
        public int B { get; set; }
        public DateTime C { get; set; }

        public override string ToString()
        {
            return $"{A} - {B} - {C}";
        }
    }


    public class Program
    {
        static void Main(string[] args)
        {
            // Assuming 2 second margin
            var margin = TimeSpan.FromSeconds(2);

            var input = new List<SomeItem>
            {
                new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 0)},
                new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 2)},
                new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 4)},
                new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 6)},
                new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 9)},
                new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 11)},
                new SomeItem() {A = 1, B = 2, C = new DateTime(2017, 6, 15, 0, 0, 15)},
                new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 2)},
                new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 4)},
                new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 6)},
                new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 9)},
                new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 11)},
                new SomeItem() {A = 1, B = 3, C = new DateTime(2017, 6, 15, 0, 0, 13)}
            };

            var firstGrouping = input.GroupBy(x => new { x.A, x.B });
            var readyForGrouping = new List<Tuple<SomeItem, int>>();

            foreach (var grouping in firstGrouping)
            {
                var data = grouping.OrderBy(z => z.C).ToList();
                var lastDate = default(DateTime?);
                var count = 0;
                var groupingCount = data.Count();
                var groupID = 0;

                while (groupingCount > count)
                {
                    groupID++;
                    readyForGrouping.AddRange(data.Skip(count).TakeWhile(z =>
                    {
                        var old = lastDate;
                        lastDate = z.C;
                        if (old == null)
                        {
                            count++;
                            return true;
                        }
                        if (z.C <= old.Value.Add(margin))
                        {
                            count++;
                            return true;
                        }
                        return false;
                    }).Select(z => new Tuple<SomeItem, int>(z, groupID)).ToList());
                }
            }

            var groupedItems = readyForGrouping.GroupBy(x => new { x.Item1.A, x.Item1.B, x.Item2 },
                x => x.Item1);

            foreach (var grouping in groupedItems)
            {
                Console.WriteLine("Start Of Group");
                foreach (var entry in grouping)
                {
                    Console.WriteLine(entry);

                }
            }
            Console.ReadLine();
        }
    }
}

您的示例輸入和輸出有點混亂。

您的前3個項目2,4和6有重疊。 4可以同時屬於2和6.這意味着要決定分組,您需要決定分組將基於哪個項目。 如果你用2開始分組,那么結果就是

  • 第1組:2,4
  • 第2組:6,
  • 第3組:9,11
  • 第4組:15

似乎你已經應用了你的人類大腦,看看如果起點是4秒,實際上6可以加入第一組。 然后這些團體成為:

  • 第1組:2,4,6
  • 第2組:9,11
  • 第3組:15

您可以創建IEqualityComparer<DateTime>來執行此操作,但是您的分組依賴於集合的順序:

public class GroupingComparer : IEqualityComparer<DateTime>
{
    private readonly int _offset;

    public GroupingComparer(int offset)
    {
        _offset = offset;
    }

    public bool Equals(DateTime x, DateTime y)
    {
        if (y.Second >= x.Second - _offset && y.Second <= x.Second + _offset) return true;

        return false;
    }

    public int GetHashCode(DateTime obj)
    {
        //Should most probably look at a better way to get the hashcode.
        return obj.ToShortDateString().GetHashCode();
    }
}

像這樣使用它:

GroupingComparer comparer = new GroupingComparer(offset:2);
var result2 = dates.GroupBy(x => x, comparer).ToList(); 

所以現在一切都取決於你想做什么。 您可以通過更改集合的順序來獲得上述任一輸出。 但是,如果您在應用程序的不同部分使用不同的順序,這可能意味着應用程序中的奇怪行為。 也許一個擴展方法OrderAndGroup可以解決這個問題。

這是不可能的,因為GroupBy將相同的項目組合在一起。 您可以添加一個“變平”時間的新屬性。

所以說public DateTime FlatDate{get{ return C.ToString("yyyy-MM-dd HH:mm");}}

您可以根據您想要分組的方式更改返回值,因此如果秒<3將其設置為0,則<6將其設置為3等。

不是最優雅,但會做你想要的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM