2010-05-11 11 views
12

mam dwa słowniki z taką samą strukturę:Jak mogę sumować wartości z dwóch słowników w 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 } 
}; 

Chciałbym podsumować wartości słowników razem i zwracają słowniki trzecie z kluczami i łącznej wartości dla każdego klucza:

Table, 9 
Chair, 10 
Couch, 9 

Moje obecne rozwiązanie polega na przechodzeniu przez słownik i wyciąganiu ich w ten sposób, ale wiem, że to rozwiązanie nie jest najbardziej wydajne lub najbardziej czytelne. Uderzam jednak w mur, próbując wymyślić rozwiązanie w LINQ.

+0

Czy to gwarantuje, że dwa słowniki będą miały ten sam zestaw kluczy? – Carlos

+0

@ Carlos w tym przypadku, tak. Ale byłoby interesujące zobaczyć rozwiązanie, w którym słowniki mają wspólne klucze, a nie inne. –

Odpowiedz

12

Poniższa nie jest najbardziej efektywnym rozwiązaniem (bo po prostu traktuje oba słowniki jako enumerables), ale to będzie działać i to jest dość jasne:

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: Dzięki za poprawkę, właśnie to sobie uświadomiłem. –

+0

Edytowałeś swoją odpowiedź, aby pokazać, jak uzyskać wynikowe zapytanie z powrotem do słownika. –

+0

@George: Dzięki –

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) 

To powinno wystarczyć.

EDIT: może być bardziej skuteczny (nie wiem, w jaki sposób przyłączyć jest realizowany)

(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

Dzięki za odpowiedź; ta odpowiedź pomaga również, jeśli chcesz obliczyć delta między obiektami. –

4

Jeśli masz gwarancję, żeliwny, że dwa zestawy klawiszy są takie same:

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

Najlepsze, co mogę wymyślić, jeśli klucze nie są identyczne:

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, nie wiem, który jest lepszy od rmant, ale jak twoje rozwiązanie nie jest czytelne?

Co złego

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

?

Właściwie uważam, że jest bardziej przejrzysty niż niektóre rozwiązania linq. Chociaż tego nie przetestowałem, myślę, że mogłaby ona mieć lepszą wydajność, ponieważ wylicza tylko klucze w jednym słowniku, a nie wartości, używałbyś rzeczywistego skrótu (lub cokolwiek jest podstawową implementacją słownika) do znajdź wartości, które są najszybszym sposobem ich zdobycia.

EDIT:

do roztworu, gdzie klucze byłby zawsze taki sam, jeśli tylko chcą dostać te współdzielone, trzeba tylko dodać linię;

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

EDIT2:

W celu uzyskania wszystkich kluczy/wartości, jeśli nie są one takie same, to byłoby tak:

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

Cóż, nadal pozostaje kwestia 'd2' posiadająca klucze, których nie ma w' d1', oczywiście ... –

+0

@Dan Tao tak, to działałoby tylko dla wspólnych kluczy. EDYCJA: dobra, dodałem do tego rozwiązanie: P –

2

Co o czymś takim?

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

Przynajmniej jest (rodzaj?) Czysty.

1

Napisałem małą metodę rozszerzenia, która połączy listę słowników z wartościami Int.Użyłem kodu z tego pytania, aby to zrobić, więc udostępniam

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))); 
    } 
Powiązane problemy