2010-07-26 19 views
8

Tôi cố gắng để xây dựng một từ điển từ đếm được, nhưng tôi cần một aggregator cho tất cả các phím có khả năng lặp lại. Sử dụng ToDictionary() trực tiếp thỉnh thoảng gây ra các khóa trùng lặp.Có cách nào tốt hơn để tổng hợp từ điển bằng LINQ không?

Trong trường hợp này, tôi có một loạt các mục nhập thời gian ({DateTime Date, double Hours}) và nếu nhiều mục nhập thời gian xuất hiện trong cùng một ngày, tôi muốn tổng thời gian cho ngày đó. Tức là, một tập hợp tùy chỉnh, sẽ cung cấp cho tôi khóa duy nhất cho mục nhập từ điển.

Có cách nào tốt hơn để thực hiện việc này không?

(Điều này không làm việc.)

private static Dictionary<DateTime, double> CreateAggregatedDictionaryByDate(IEnumerable<TimeEntry> timeEntries) 
    { 
     return 
      timeEntries 
       .GroupBy(te => new {te.Date}) 
       .Select(group => new {group.Key.Date, Hours = group.Select(te => te.Hours).Sum()}) 
       .ToDictionary(te => te.Date, te => te.Hours); 
    } 

Tôi nghĩ rằng tôi đang thực sự tìm kiếm một cái gì đó như thế này:

IEnumerable<T>.ToDictionary( 
    /* key selector : T -> TKey */, 
    /* value selector : T -> TValue */, 
    /* duplicate resolver : IEnumerable<TValue> -> TValue */); 

như vậy ...

timeEntries.ToDictionary( 
    te => te.Date, 
    te => te.Hours, 
    duplicates => duplicates.Sum()); 

Các 'resolver 'có thể là .irst() hoặc .Max() hoặc bất kỳ thứ gì.

Hoặc một cái gì đó tương tự.


Tôi đã có một triển khai ... và một số khác xuất hiện trong các câu trả lời trong khi tôi đang thực hiện.

Mine:

public static Dictionary<TKey, TValue> ToDictionary<T, TKey, TValue>(
     this IEnumerable<T> input, 
     Func<T, TKey> keySelector, 
     Func<T, TValue> valueSelector, 
     Func<IEnumerable<TValue>, TValue> duplicateResolver) 
    { 
     return input 
      .GroupBy(keySelector) 
      .Select(group => new { group.Key, Value = duplicateResolver(group.Select(valueSelector)) }) 
      .ToDictionary(k => k.Key, k => k.Value); 
    } 

Tôi đã hy vọng có một cái gì đó như thế đã có, nhưng tôi đoán không. Đó sẽ là một bổ sung tốt đẹp.

Cảm ơn tất cả mọi người :-)

+0

Bạn có nghĩa là bạn muốn uniquify chìa khóa, hay bạn muốn loại bỏ các dups? – Abel

+0

Tôi đã cập nhật mô tả. Cố gắng tổng hợp các bản sao để làm cho chúng trở thành duy nhất và sau đó xây dựng một từ điển từ đó. –

Trả lời

5
public static Dictionary<KeyType, ValueType> ToDictionary 
    <SourceType, KeyType, ValueType> 
(
    this IEnumerable<SourceType> source, 
    Func<SourceType, KeyType> KeySelector, 
    Func<SourceType, ValueType> ValueSelector, 
    Func<IGrouping<KeyType, ValueType>, ValueType> GroupHandler 
) 
{ 
    Dictionary<KeyType, ValueType> result = source 
    .GroupBy(KeySelector, ValueSelector) 
    .ToDictionary(g => g.Key, GroupHandler); 
} 

gọi theo:

Dictionary<DateTime, double> result = timeEntries.ToDictionary(
    te => te.Date, 
    te => te.Hours, 
    g => g.Sum() 
); 
3

Nếu phím trùng lặp là một vấn đề, có lẽ bạn có nghĩa là ToLookup? Cùng gốc, nhưng nhiều giá trị cho mỗi khoá ...

private static ILookup<DateTime, double> CreateAggregatedDictionaryByDate(IEnumerable<TimeEntry> timeEntries) 
{ 
    return 
     timeEntries 
      .GroupBy(te => new {te.Date}) 
      .Select(group => new {group.Key.Date, Hours = group.Select(te => te.Hours).Sum()}) 
      .ToLookup(te => te.Date, te => te.Hours); 
} 

Sau đó, bạn chỉ cần làm một cái gì đó như:

var lookup = CreateAggregatedDictionaryByDate(...); 
foreach(var grp in lookup) { 
    Console.WriteLine(grp.Key); // the DateTime 
    foreach(var hours in grp) { // the set of doubles per Key 
     Console.WriteLine(hours) 
    } 
} 

hoặc sử dụng SelectMany tất nhiên (from...from).

0

Nếu bạn acess indexer của một từ điển và không có gì có, nó cho phép bạn thiết lập nó sẽ trả về một cấu trúc mặc định của kiểu dữ liệu, trong trường hợp của một đôi nó sẽ là 0. Tôi có lẽ sẽ làm một cái gì đó giống như

public void blabla(List<TimeEntry> hoho) 
{ 
    Dictionary<DateTime, double> timeEntries = new Dictionary<DateTime, double>(); 
    hoho.ForEach((timeEntry) => 
     { 
      timeEntries[timeEntry.Day] = 0; 
     }); 

    hoho.ForEach((timeEntry) => 
     { 
      timeEntries[timeEntry.Day] += timeEntry.Hours; 
     }); 

} 

Chỉ cần sử dụng Danh sách vì không rõ lý do, phần mở rộng .ForEach() không được triển khai trên ienumerable, mặc dù tôi sẽ tưởng tượng việc thực hiện sẽ là dòng cho giống hệt nhau, nhưng bạn chỉ có thể làm một foreach() những gì nó làm dưới bao gồm anyway.

Tôi nghĩ từ quan điểm dễ đọc, điều này sẽ giúp bạn dễ dàng thực hiện những gì đang được thực hiện, trừ khi đây không phải là những gì bạn đang cố gắng làm ..

+2

Tạo 'KeyNotFoundException: Khóa đã cho không có trong từ điển' trong lệnh' timeEntries [] + = '. Bạn cần phải khởi tạo giá trị từ điển trước khi bạn có thể sử dụng + = trên nó. –

+0

Ah đúng Sam, ngớ ngẩn tôi, cố định chỉnh sửa ngay bây giờ .. –

0

Tôi thích phương pháp của bạn vì nó rõ ràng, nhưng bạn muốn làm cho nó hiệu quả hơn, bạn có thể thực hiện các thao tác sau để thực hiện tổng hợp và nhóm trong một cuộc gọi Aggregate đơn lẻ.

private static Dictionary<DateTime, double> CreateAggregatedDictionaryByDate(IEnumerable<TimeEntry> timeEntries) 
{ 
    return timeEntries.Aggregate(new Dictionary<DateTime, double>(), 
           (accumulator, entry) => 
            { 
             double value; 
             accumulator.TryGetValue(entry.Date, out value); 
             accumulator[entry.Date] = value + entry.Hours; 
             return accumulator; 
            }); 
} 
+1

Tốt. Một chút phức tạp ... nhưng yeah. Tôi đoán tôi không thực sự chắc chắn những gì tôi đang tìm kiếm. Có lẽ một quá tải cho ToDictionary() cung cấp một tham số thứ ba để giải quyết các bản sao? –

0

Bạn đang tìm kiếm nội dung như thế này?

private static Dictionary<DateTime, double> CreateAggregatedDictionaryByDate(IEnumerable<TimeEntry> timeEntries) 
{ 
    return 
     (from te in timeEntries 
     group te by te.Date into grp) 
     .ToDictionary(grp => grp.Key, (from te in grp select te.Hours).Sum()); 
} 
+0

Vâng, đó là chính xác những gì tôi có, chỉ hoàn toàn với cú pháp phương pháp mở rộng. –

+0

Mỏ khác ở chỗ nó đặt tổng hợp vào cuộc gọi 'ToDictionary', thay vì tính toán nó trước. – Gabe

+0

Ồ, tôi hiểu rồi. Hoàn toàn bỏ lỡ điều đó. Nice, cảm ơn. –

Các vấn đề liên quan