2009-05-15 4 views
5

zdarza mi się korzystać z tego rodzaju struktury sporo:Jak mogę zmniejszyć ból inicjalizacji słowników list w C#?

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

Co prowadzi do tego rodzaju kodu:

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

Czy uważasz, że warto pisać niestandardowej klasy DictionaryOfList która będzie obsługiwać to rodzaj rzeczy automatycznie?

Czy istnieje inny sposób leniwego inicjowania tych list?

+0

Co trochę rzeczą nie znajdziesz to struktura przydatna? będąc całkiem nowym programistą, myślę, że jeszcze nie użyłem struktury słownika tak bardzo! – RYFN

+0

Informacje o wersji ramowej byłyby użyteczne, LINQ może sprawić, że będzie to dużo czystsze. – AnthonyWJones

+0

@Anthony: Rzeczywiście. edytowane znaczniki wspominające .net3.5 – Brann

Odpowiedz

7

Możesz napisać metodę rozszerzenia - GetValueOrCreateDefault() lub coś podobnego:

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

Może nawet napisać metodę rozszerzenia dla całej inicjalizacji?

+0

Fajne rozwiązanie, ale chciałbym pójść dalej i stworzyć metodę słownikową .AddPair (TKey, TValue). Będzie to znacznie łatwiejsze do odczytania i zrozumienie, jeśli ktoś inny będzie musiał przejrzeć kod. –

2

myślę dodaje powinien zrobić:

class DictionaryOfList : Dictionary<string, List<string>> {} 
  • Edycja I powinien przeczytać poprawnie. To nie odpowiada na pytanie. Tanascius dostarczył schludny sposób, aby go rozwiązać.
+0

Dzięki, to jest przydatne! – Nick

4

Słownik listy ... w .NET 3.5, który byłby ILookup<TKey,TValue>. Domyślna implementacja (Lookup<TKey,TValue>) jest niezmienna, ale napisałem EditableLookup<TKey,TValue> dla MiscUtil. To będzie dużo prostsze w obsłudze - czyli

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 
} 

Poza tym, metodę rozszerzenia:

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

Nice! I <4 metody rozszerzania. –

+0

Rzeczywiście ładne, chociaż nadal nie jestem przekonany, jeśli chodzi o łatwość konserwacji i niejasne rzeczy, które można z tym zrobić. Na przykład implementowanie domyślnych implementacji interfejsów przy użyciu metod rozszerzeń. Ale w tym przypadku bardzo przydatny! – Kevin

1

dodać odwołanie do System.Data.DataSetExtensions i można używać rozszerzeń 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()); 

nie jestem pewien, czy męczyć się z innej klasy, ale metoda narzędzie lub rozszerzenie mogłoby uczynić to prostsze:

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

Czy widzisz ToLookup? –

0

Nie zapomnij o the using directive.

Nie odpowiada to bezpośrednio, ale może być pomocne. "Używanie aliasu" dla ogólnego typu kolekcji może sprawić, że kod będzie łatwiejszy dla oczu.

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 za tę podpowiedź.

0

Dlaczego nie tylko uproszczenie trochę:

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

Powodem tego nie jest to, że złożoność byłaby O (2n) zamiast O (n). Oczywiście w większości przypadków to chyba nie jest problem. – Brann