2010-05-11 20 views
12

Ich habe zwei Wörterbücher mit der gleichen Struktur:Wie summiere ich Werte aus zwei Wörterbüchern in C#?

Dictionary<string, int> foo = new Dictionary<string, int>() 
{ 
    {"Table", 5 }, 
    {"Chair", 3 }, 
    {"Couch", 1 } 
}; 

Dictionary<string, int> bar = new Dictionary<string, int>() 
{ 
    {"Table", 4 }, 
    {"Chair", 7 }, 
    {"Couch", 8 } 
}; 

Ich mag die Werte des Wörterbücher summieren zusammen und kehre die Tasten einen dritten Wörterbücher mit, und die Gesamtwert für jede Taste:

Table, 9 
Chair, 10 
Couch, 9 

Meine aktuelle Lösung ist es, das Wörterbuch zu durchlaufen und sie auf diese Weise herauszuziehen, aber ich weiß, dass diese Lösung nicht die performanteste oder lesbarste ist. Ich stoße jedoch auf eine Mauer, um in LINQ eine Lösung zu finden.

+0

Ist es garantiert, dass die zwei Wörterbücher den gleichen Satz Schlüssel haben werden? – Carlos

+0

@Carlos in diesem Fall, ja. Aber es wäre interessant, eine Lösung zu sehen, bei der die Wörterbücher einige Schlüssel teilen und nicht andere. –

Antwort

12

Das folgende ist nicht die effizienteste Lösung (weil es einfach beide Wörterbücher als enumerables behandelt), aber es funktioniert, und es ist ganz klar:

Dictionary<string, int> result = (from e in foo.Concat(bar) 
       group e by e.Key into g 
       select new { Name = g.Key, Count = g.Sum(kvp => kvp.Value) }) 
       .ToDictionary(item => item.Name, item => item.Count); 
+0

@Ben: Danke für die Korrektur, das habe ich gerade auch gemerkt. –

+0

Bearbeitet Ihre Antwort, um zu zeigen, wie die resultierende Abfrage in ein Wörterbuch zurückgeschrieben wird. –

+0

@George: Danke –

4
(from a in foo 
join b in bar on a.Key equals b.Key 
select new { Key = a.Key, Value = a.Value + b.Value }) 
.ToDictionary(a => a.Key,a => a.Value) 

Das sollte es tun.

EDIT: Könnte effizienter sein (nicht sicher, wie die Verbindung implementiert wird)

(from a in foo 
let b = bar.ContainsKey(a.Key) ? (int?)bar[a.Key] : null 
select new { Key = a.Key, Value = a.Value + (b != null ? b : 0) } 
).ToDictionary(a => a.Key, a => a.Value) 
+0

Danke für die Antwort; Diese Antwort hilft auch, wenn Sie Deltas zwischen den Objekten berechnen möchten. –

4

Wenn Sie eine Gusseisen Garantie, dass die beiden Sätze von Tasten sind die gleichen:

Dictionary<string, int> Res2 = foo.ToDictionary(orig => orig.Key, orig => orig.Value + bar[orig.Key]); 

Beste, was ich konnte kommen mit, wenn Schlüssel nicht gleiche Serie:

var AllKeys = foo.Keys.Union(bar.Keys); 
var res3 = AllKeys.ToDictionary(key => key, key => (foo.Keys.Contains(key)?foo[key] : 0) + (bar.Keys.Contains(key)?bar[key] : 0)); 
3

Mmm, ich weiß nicht, was mehr pro fo Aber wie ist Ihre Lösung nicht lesbar?

Was stimmt nicht mit

foreach (string key in d1.Keys) 
    { 
    d3.Add(key,d1[key]+d2[key]); 
    } 

?

Ich denke eigentlich, es ist klarer als einige der Linq-Lösungen. Obwohl ich es nicht getestet habe, denke ich, dass es eine bessere Leistung haben könnte, da es nur die Schlüssel in einem Wörterbuch aufzählt und nicht die Werte, Sie würden das tatsächliche Hashing (oder was auch immer die zugrunde liegende Implementierung des Wörterbuchs ist) verwenden finde die Werte, was der schnellste Weg ist, sie zu bekommen.

EDIT:

für die Lösung, wo Schlüssel würde nicht immer gleich sein, wenn Sie geteilt diejenigen erhalten möchten, müssen Sie nur eine Zeile hinzufügen müssen;

foreach (string key in d1.Keys) 
    { 
    if(d2.ContainsKey(key) 
     d3.Add(key,d1[key]+d2[key]); 
    } 

EDIT2:

Um alle Schlüssel/Werte zu erhalten, wenn sie nicht gleich sind, dann würde es so aussehen:

foreach (string key in d1.Keys) 
     { 
     if(d2.ContainsKey(key) 
      d3.Add(key,d1[key]+d2[key]); 
     else 
      d3.Add(key,d1[key]) 
     } 

    foreach (string key in d2.keys) 
     { 
      if(!d1.ContainsKey(key) // only get keys that are unique to d2 
      d3.Add(key,d2[key]); 
     } 
+0

Nun, es ist immer noch die Sache von 'd2' mit Schlüsseln, die nicht in' d1' sind, natürlich ... –

+0

@Dan Tao Ja, das würde nur für die gemeinsamen Schlüssel funktionieren. EDIT: Okay, ich habe die Lösung dafür hinzugefügt: P –

2

Was ist so etwas wie das?

var fooBar = foo.Keys 
    .Union(bar.Keys) 
    .Select(
     key => { 
      int fval = 0, bval = 0; 

      foo.TryGetValue(key, out fval); 
      bar.TryGetValue(key, out bval); 

      return new KeyValuePair<string, int>(key, fval + bval); 
     } 
    ) 
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); 

Zumindest ist es (irgendwie?) Ordentlich.

1

Ich schrieb eine kleine Erweiterungsmethode, die eine Liste von Wörterbüchern mit Int-Werten zusammenführen wird.Ich benutzte Code von dieser Frage, um es zu tun, damit ich teile

public static Dictionary<TSource, Int32> MergeIntDictionary<TSource>(this ICollection<Dictionary<TSource, Int32>> source) 
    { 
     return source.Aggregate((cur, next) => cur.Concat(next) 
      .GroupBy(o => o.Key) 
      .ToDictionary(item => item.Key, item => item.Sum(o => o.Value))); 
    } 
Verwandte Themen