2010-05-11 64 views
12

Tengo dos diccionarios con la misma estructura:¿Cómo puedo sumar valores de dos diccionarios en 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 } 
}; 

me gustaría resumir los valores de los diccionarios juntos y volver a terceros diccionarios con las claves y los valores totales para cada tecla:

Table, 9 
Chair, 10 
Couch, 9 

Mi solución actual es recorrer el diccionario y sacarlos de esa manera, pero sé que esa solución no es la más eficaz ni la más legible. Sin embargo, estoy golpeando una pared de ladrillos tratando de encontrar una solución en LINQ.

+0

¿Se garantiza que los dos diccionarios tendrán el mismo conjunto de claves? – Carlos

+0

@Carlos en este caso, sí. Pero sería interesante ver una solución donde los diccionarios compartan algunas claves y otras no. –

Respuesta

12

La siguiente no es la solución más eficiente (ya que simplemente trata a ambos diccionarios como enumerables), pero funciona y es bastante claro:

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: Gracias por la corrección, me di cuenta de eso también. –

+0

Editó su respuesta para mostrar cómo devolver la consulta resultante a un diccionario. –

+0

@George: Gracias –

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) 

Eso debería hacerlo.

EDIT: ¿Podría ser más eficiente (no sé cómo la unión está implementado)

(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

Gracias por la respuesta; esta respuesta también ayuda si desea calcular deltas entre los objetos. –

4

Si usted tiene una garantía de hierro fundido que los dos juegos de llaves son los mismos:

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

lo mejor que podría llegar a si las claves no son mismo conjunto:

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, no sé que es más por fo rmant, pero ¿cómo es que tu solución no es legible?

Cuál es incorrecto con

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

?

De hecho, creo que es más claro que algunas de las soluciones de linq. Aunque no lo he probado, creo que podría tener un mejor rendimiento, ya que solo enumera las claves en un diccionario y no los valores, usaría el hash real (o lo que sea la implementación subyacente del diccionario) para encuentre los valores, que es la forma más rápida de obtenerlos.

EDIT:

para la solución donde las claves no estarían siempre el mismo, si sólo se desea conseguir los compartidos, sólo tiene que añadir una línea;

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

Edit2:

Con el fin de conseguir todas las llaves/valores si no son los mismos, entonces sería como esto:

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

Bueno, todavía está la cuestión de 'd2' tener claves que no están en' d1', por supuesto ... –

+0

@Dan Tao sí, eso solo funcionaría para las claves compartidas. EDITAR: de acuerdo, agregué la solución para eso: P –

2

¿Qué pasa algo como esto?

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

por lo menos es (especie de?) Puro.

1

Escribí un pequeño método de extensión que combinará una lista de diccionarios con valores Int.Utilicé el código de esta pregunta para hacerlo, así que estoy compartiendo

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