2010-07-26 3 views
8

열거 형에서 사전을 작성하려고하지만 모든 중복 키가있는 집계가 필요합니다. ToDictionary()를 직접 사용하면 때때로 중복 키가 발생합니다.LINQ를 사용하여 사전을 집계하는 더 좋은 방법이 있습니까?

이 경우 시간 항목 ({DateTime Date, double Hours})이 여러 개 있고 같은 날짜에 여러 시간 항목이있는 경우 해당 날짜의 총 시간을 원합니다. 즉, 사전 집계에 대한 고유 키를 제공하는 맞춤 애그리 게이터입니다.

이보다 더 좋은 방법이 있습니까?

(이 작업을 수행합니다.)

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

내가 정말 이런 식으로 뭔가를 찾고 있어요 생각 : 그래서

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

...

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

'해결 '는 .First() 또는 .Max() 또는 무엇이든 될 수 있습니다.

또는 이와 유사한 것.


나는 하나의 구현을 가지고 있었고, 다른 하나는 내가 그 일을하는 동안 답변에 나타났습니다.

광산 : 나는 그런 일이 이미 있었다 기대했다,하지만 난하지 추측

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

. 그건 좋은 추가 것입니다.

+0

당신이 키를 uniquify 할, 또는 당신이 DUPS을 제거 할 할 것을 의미합니까? – Abel

+0

설명이 업데이트되었습니다. 고유 한 것을 만들기 위해 중복을 모으고, 그로부터 사전을 만든다. –

답변

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

에 의해 호출 :

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

:-)

덕분에 모두가 중복 키 아마 당신은 ToLookup을 의미, 문제가되는 경우? 같은 교장,하지만 키에 여러 값 ...

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

그런 다음 당신은 단순히 같은 것을 할 : 물론

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

또는 사용 SelectMany (from...from을). 당신이 사전의 인덱서를 ACESS 거기에 아무것도 경우

0

, 그것은 당신이 내가 어쩌면

처럼 뭔가를 할 것이라고 0을 수 있습니다 더블의 경우,이 데이터 형식의 기본 구조를 반환 설정할 수 있습니다
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; 
     }); 

} 

모호한 이유 때문에 .ForEach() 확장이 ienumerable에 구현되지 않았으므로, 구현이 라인 일치와 같을 것이라고 생각되지만 리터럴 foreach()를 수행 할 수 있습니다. 어쨌든 그것은 덮개 밑에서 무엇을합니다.

가독성 관점에서 볼 때, 이것은 당신이하려고했던 것이 아닌 한, 수행되고있는 것보다 훨씬 쉽습니다.

+2

'keyNotFoundException : 주어진 key가'timeEntries [] + ='호출시 사전에 존재하지 않는다. + =를 사용하기 전에 사전 값을 초기화해야합니다. –

+0

아, 샘, 어리 석음, 지금 편집 수정. –

0

당신의 방법이 마음에 드는데, 당신이 그것을 좀더 효율적으로 만들고 싶다면 약간의 회선이긴하지만 하나의 통화로 모든 집계와 그룹화를 할 수 있습니다.

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

니스. 조금 복잡하지만 ... 그래. 내가 뭘 찾고 있는지 잘 모르겠다. 중복을 해결하는 세 번째 매개 변수를 제공하는 ToDictionary()에 대한 과부하 일 수 있습니까? –

0

이와 비슷한 제품을 찾고 계십니까?

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

그래, 그게 내가 가진거야, 순수하게 확장 메서드 구문. –

+0

광산은 집계를 먼저 계산하는 것이 아니라 'ToDictionary' 호출에 집계한다는 점이 다릅니다. – Gabe

+0

오, 알았어요. 완전히 그걸 놓쳤다. 좋았어. 고마워. –

관련 문제