2009-05-15 6 views
5

que suceda a utilizar este tipo de estructura bastante:¿Cómo puedo aliviar el dolor de inicializar diccionarios de listas en C#?

Dictionary<string, List<string>> Foo = new Dictionary<string, List<string>>(); 

que nos lleva a este tipo de código:

foreach (DataRow dr in ds.Tables[0].Rows) 
{ 
    List<string> bar; 
    if (!Foo.TryGetValue(dr["Key"].ToString(), out desks)) 
    { 
     bar= new List<string>(); 
     Foo.Add(dr["Key"].ToString(), bar); 
    } 
    bar.Add(dr["Value"].ToString()); 
} 

¿Crees que vale la pena escribir una clase DictionaryOfList a medida que manejar esto tipo de cosas de forma automática?

¿Hay alguna otra forma de inicializar las Listas de forma lenta?

+0

¿Qué clase de cosa ¿Se encuentra esta estructura útil para? siendo un programador relativamente nuevo, ¡creo que todavía no he usado demasiado la estructura del diccionario! – RYFN

+0

La información de la versión del marco sería útil, LINQ podría hacer esto mucho más limpio – AnthonyWJones

+0

@Anthony: De hecho. etiquetas editadas para mencionar .net3.5 – Brann

Respuesta

7

Se puede escribir un método de extensión - GetValueOrCreateDefault() o algo por el estilo:

foreach (DataRow dr in ds.Tables[0].Rows) 
{ 
    Foo.GetValueOrCreateDefault(dr["Key"]).Add(dr["Value"].ToString()) 
} 

Tal vez incluso se puede escribir un método de extensión para toda la inicialización?

+0

Buena solución, pero iría más allá y crearía un método de extensión Dictionary .AddPair (TKey, TValue). Esto será mucho más fácil de leer y comprender si alguien más tiene que revisar el código. –

2

creo que el siguiente debe hacer:

class DictionaryOfList : Dictionary<string, List<string>> {} 
  • Editar que debería leer más adecuadamente. Esto no responde la pregunta. Tanascius ha proporcionado una manera ordenada de solucionarlo.
+0

¡Gracias, eso es práctico! – Nick

4

Un diccionario de una lista ... en .NET 3.5 que sería ILookup<TKey,TValue>. La implementación predeterminada (Lookup<TKey,TValue>) es inmutable, pero escribí EditableLookup<TKey,TValue> para MiscUtil. Esto va a ser mucho más fácil de usar - es decir

var data = new EditableLookup<string, int>(); 
data.Add("abc",123); 
data.Add("def",456); 
data.Add("abc",789); 

foreach(int i in data["abc"]) { 
    Console.WriteLine(i); // 123 & 789 
} 

Aparte de eso, un método de extensión:

public static void Add<TKey, TList, TValue>(
    this IDictionary<TKey, TList> lookup, 
    TKey key, TValue value) 
    where TList : class, ICollection<TValue>, new() 
{ 
    TList list; 
    if (!lookup.TryGetValue(key, out list)) 
    { 
     lookup.Add(key, list = new TList()); 
    } 
    list.Add(value); 
} 

static void Main() { 
    var data = new Dictionary<string, List<string>>(); 
    data.Add("abc", "def"); 
} 
+0

¡Agradable! I <4 métodos de extensión. –

+0

De hecho, agradable, aunque todavía no estoy muy convencido cuando se trata de mantenibilidad y cosas oscuras que se pueden hacer con él. Por ejemplo, implementar implementaciones predeterminadas para interfaces que usan métodos de extensión. ¡Pero en este caso es muy útil! – Kevin

1

agregar una referencia a System.Data.DataSetExtensions y se puede usar las extensiones de LINQ:

var dictOfLst = ds.Tables[0].Rows. 
    //group by the key field 
    GroupBy(dr => dr.Field<string>("key")). 
    ToDictionary(
     grp => grp.Key, 
     //convert the collection of rows into values 
     grp => grp.Select(dr => dr.Field<string>("value")).ToList()); 

no estoy seguro de que me molesté con otra clase, sino un método de utilidad o la extensión podría hacerlo mas simple:

public static Dictionary<TKey, List<TValue>> ToGroupedDictionary<TKey, List<TValue>>(
    this DataTable input, 
    Func<TKey, DataRow> keyConverter, 
    Func<TValue, DataRow> valueConverter) 
{ 
    return input.Rows. 
     //group by the key field 
     GroupBy(keyConverter). 
     ToDictionary(
      grp => grp.Key, 
      //convert the collection of rows into values 
      grp => grp.Select(valueConverter).ToList()); 
} 

//now you have a simpler syntax 
var dictOfLst = ds.Tables[0].ToGroupedDictionary(
    dr => dr.Field<string>("key"), 
    dr => dr.Field<string>("value")); 
+1

¿Has visto ToLookup? –

0

No se olvide de the using directive.

Esto no responde directamente, pero puede ser útil de todos modos. Un "uso de alias" para un tipo de colección genérico puede hacer que su código sea más fácil para los ojos.

using StoreBox = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>>; 
using ListOfStrings = System.Collections.Generic.List<string>; 
class Program 
{ 
    static void Main(string[] args) 
    { 
     var b = new StoreBox(); 
     b.Add("Red", new ListOfStrings {"Rosso", "red" }); 
     b.Add("Green", new ListOfStrings {"Verde", "green" }); 
    } 
} 

Credit to SO para esta sugerencia.

0

¿Por qué no simplificar un poco:

foreach (DataRow dr in ds.Tables[0].Rows) 
{ 
    string key = dr["Key"].ToString(); 
    if (!Foo.ContainsKey(key)) Foo.Add(key, new List<string>()); 
    Foo[key].Add(dr["Value"].ToString()); 
} 
+0

Una razón para no hacer eso es que la complejidad sería O (2n) en lugar de O (n). Por supuesto, en la mayoría de los casos, eso probablemente no sea realmente un problema. – Brann

Cuestiones relacionadas