C # LINQ - сортировать и группировать словарьпо дате с максимальным размером группы - PullRequest
1 голос
/ 23 февраля 2011

Я ищу созданные партии из Dictionary<string, DateTime> со следующими ограничениями:

  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;
                    }
                }
            }
        }
    }
}

Ответы [ 3 ]

1 голос
/ 23 февраля 2011

Вы группируете по вашему ключу, затем внутри результата вы группируете по индексу элемента, деленному на желаемый размер куска.

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);
}
0 голосов
/ 23 февраля 2011

Вы можете сделать это с 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;
}

Надеюсь, это поможет.

0 голосов
/ 23 февраля 2011

Не строго используя 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...