【问题标题】:C# LINQ - sort and group a Dictionary<string,DateTime> by date with maximum group sizeC# LINQ - 按日期对 Dictionary<string,DateTime> 进行排序和分组,最大组大小
【发布时间】:2011-02-23 10:39:43
【问题描述】:

我希望从 Dictionary&lt;string, DateTime&gt; 创建具有以下约束的批次:

  1. 批次中的所有项目都具有相同的日期
  2. 一个批次不能超过 X 个项目。如果有更多具有相同日期的项目,则必须创建另一个批次。

我已经制定了以下逻辑,但想知道是否有其他更简洁的方法可以仅使用 linq。

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

namespace dictionary_sort_by_value_test
{
    class Program
    {
        static void Main(string[] args)
        {
            int maxBatchSize = 3;

            Dictionary<string, DateTime> secs = new Dictionary<string, DateTime>();
            secs.Add("6571 JT", new DateTime(2011, 1, 10));
            secs.Add("6572 JT", new DateTime(2011, 1, 12));
            secs.Add("6573 JT", new DateTime(2011, 1, 12));
            secs.Add("6574 JT", new DateTime(2011, 1, 12));
            secs.Add("6575 JT", new DateTime(2011, 1, 10));
            secs.Add("6576 JT", new DateTime(2011, 1, 11));
            secs.Add("6577 JT", new DateTime(2011, 1, 11));
            secs.Add("6578 JT", new DateTime(2011, 1, 11));
            secs.Add("6579 JT", new DateTime(2011, 1, 11));

            var sorted = secs.OrderBy(o => o.Value).GroupBy(o => o.Value);

            foreach (var date in sorted)
            {    
                Console.Write("\nNew batch at {0} \n", date.Key);
                int batchsize = 0;
                foreach (var sec in date)
                {
                    if (batchsize < maxBatchSize)
                    {
                        Console.Write("  {0} {1} \n", sec.Key, sec.Value);
                        batchsize++;
                    }
                    else
                    {
                        Console.Write("\nNew batch at {0} \n", date.Key);
                        Console.Write("  {0} {1} \n", sec.Key, sec.Value);
                        batchsize = 1;
                    }
                }
            }
        }
    }
}

【问题讨论】:

  • 我也看不到您在哪里/如何在代码中创建新批次。您正在计算批次的数量,但不是每次都创建一个新批次并将其添加到您的“结果”中。就我个人而言,仅使用 LINQ 可以做到这一点的唯一方法是拥有另一个决胜局,这样你也可以按此分组。或者将您的逻辑实现为 IEnumerable 上的扩展方法。这样一来,它看起来像 LINQ,感觉像 LINQ,它将是 LINQ,因为无论如何 LINQ 都是这样实现的。

标签: linq datetime sorting group-by


【解决方案1】:

您按键分组,然后在结果中按项目索引除以所需的块大小进行分组。

var chunkSize = 3;
var sorted = secs
    .OrderBy(kv => kv.Key)
    .GroupBy(o => o.Value)
    .Select(g => new {Chunks = g.Select((o,i) => new {Val = o, Index = i})
                                .GroupBy(item => item.Index / chunkSize)});

并显示它:

 foreach(var item in sorted.SelectMany(item => item.Chunks))
 {
     Console.WriteLine("New batch at " + item.First().Val.Value);
     foreach(var element in item)
         Console.WriteLine(element.Val.Key);
}

【讨论】:

  • 谢谢。这正是我想要的。
  • 看起来不太好看,但我喜欢它并且它有效。
【解决方案2】:

不是严格使用 linq 来解决您的问题,而是一种更简洁的处理迭代的方式:

static void Main(string[] args)
{
    int maxBatchSize = 3;

    Dictionary<string, DateTime> secs = new Dictionary<string, DateTime>();
    secs.Add("6571 JT", new DateTime(2011, 1, 10));
    secs.Add("6572 JT", new DateTime(2011, 1, 12));
    secs.Add("6573 JT", new DateTime(2011, 1, 12));
    secs.Add("6574 JT", new DateTime(2011, 1, 12));
    secs.Add("6575 JT", new DateTime(2011, 1, 10));
    secs.Add("6576 JT", new DateTime(2011, 1, 11));
    secs.Add("6577 JT", new DateTime(2011, 1, 11));
    secs.Add("6578 JT", new DateTime(2011, 1, 11));
    secs.Add("6574 JT", new DateTime(2011, 1, 11));
    secs.Add("6579 JT", new DateTime(2011, 1, 11));
    secs.Add("6580 JT", new DateTime(2011, 1, 11));
    secs.Add("6581 JT", new DateTime(2011, 1, 11));
    secs.Add("6582 JT", new DateTime(2011, 1, 11));
    secs.Add("6583 JT", new DateTime(2011, 1, 11));

    secs.OrderBy(o => o.Value).GroupBy(o => o.Value).ToList().ForEach(date =>
                   {
                       Console.Write("\nNew batch at {0} \n", date.Key);
                       int batchsize = 0;
                       foreach (var sec in date)
                       {
                           if (batchsize >= maxBatchSize)
                           {
                               Console.Write("\nNew batch at {0} \n", date.Key);
                               batchsize = 0;
                           }

                           Console.Write("  {0} {1} \n", sec.Key, sec.Value);
                           batchsize++;
                       }
                   });

    Console.ReadLine();
}

【讨论】:

    【解决方案3】:

    您可以使用 2 个 GroupBys 来做到这一点。首先按日期时间分组,然后按页分组。我必须明确指定泛型参数,因为编译器选择了错误的重载,这使得查询代码更长。

    var groups = secs.GroupBy<KeyValuePair<string, DateTime>, DateTime, string, Group>(
        p => p.Value,
        p => p.Key,
        (d, g) => new Group {
            Date = d,
            Pages = g.Select((s, i) => new KeyValuePair<string, int>(s, i / maxBatchSize))
                .GroupBy<KeyValuePair<string, int>, int, string, Page>(
                    p => p.Value,
                    p => p.Key,
                    (p, g2) => new Page { Id = p, Items = g2.ToList() }) });
    
    foreach (var group in groups)
    {
        Console.WriteLine("Date: {0}", group.Date);
        foreach (var page in group.Pages)
        {
            Console.WriteLine("Page: {0}", page.Id);
            foreach (var key in page.Items)
                Console.WriteLine(key);
        }
    }
    

    如您所见,我必须定义 2 个类,因为正如我所说,我必须指定泛型参数,因为使用匿名类型会使重载决议选择另一个重载。

    class Group
    {
        public DateTime Date;
        public IEnumerable<Page> Pages;
    }
    
    class Page
    {
        public int Id;
        public IEnumerable<string> Items;
    }
    

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2017-06-13
      • 2017-09-04
      • 1970-01-01
      • 2021-12-01
      • 2015-09-28
      • 2017-08-01
      • 2020-05-20
      • 1970-01-01
      • 2013-12-27
      相关资源
      最近更新 更多