Jaki jest najlepszy sposób scalenia 2 lub więcej słowników (Dictionary<T1,T2>
) w C #? (Funkcje 3.0, takie jak LINQ, są w porządku).
Myślę o sygnaturze metody w następujący sposób:
public static Dictionary<TKey,TValue>
Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);
lub
public static Dictionary<TKey,TValue>
Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);
EDYCJA: Mam fajne rozwiązanie od JaredPara i Jona Skeeta, ale myślałem o czymś, co obsługuje duplikaty kluczy. W przypadku kolizji nie ma znaczenia, która wartość jest zapisywana w dykcie, o ile jest spójna.
c#
dictionary
merge
orip
źródło
źródło
dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value)
.Odpowiedzi:
Zależy to częściowo od tego, co chcesz się wydarzyć, jeśli napotkasz duplikaty. Na przykład możesz wykonać:
Spowoduje to wyjątek, jeśli otrzymasz duplikaty kluczy.
EDYCJA: Jeśli użyjesz ToLookup, otrzymasz odnośnik, który może mieć wiele wartości na klucz. Państwo mogłoby następnie przekonwertować do słownika:
Jest to trochę brzydkie - i nieefektywne - ale to najszybszy sposób na zrobienie tego pod względem kodu. (Wprawdzie go nie testowałem).
Możesz oczywiście napisać własną metodę rozszerzenia ToDictionary2 (o lepszej nazwie, ale nie mam teraz czasu na wymyślenie jednej) - nie jest to strasznie trudne, po prostu nadpisywanie (lub ignorowanie) duplikatów kluczy. Ważną rzeczą (moim zdaniem) jest użycie SelectMany i uświadomienie sobie, że słownik obsługuje iterację nad parami klucz / wartość.
źródło
GroupBy
może rzeczywiście być nieco bardziej wydajne - ale wątpię, czy i tak byłoby znaczące.ToDictionary
wywołanie metody. Wolę jednak uprościć odpowiedź w przypadku bardziej powszechnego przypadku i mam nadzieję, że każdy, kto potrzebuje niestandardowego urządzenia porównującego, jest w stanie znaleźć odpowiednie przeciążenie.Zrobiłbym to w ten sposób:
Proste i łatwe. Zgodnie z tym postem na blogu jest nawet szybszy niż większość pętli, ponieważ jego podstawowa implementacja uzyskuje dostęp do elementów według indeksu, a nie modułu wyliczającego (zobacz tę odpowiedź) .
Będzie oczywiście rzucał wyjątek, jeśli istnieją duplikaty, więc musisz to sprawdzić przed scaleniem.
źródło
dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value)
. W ten sposób wartości zdictionaryFrom
przesłaniają wartość dla potencjalnie istniejącego klucza.Nie wybuchnie to, jeśli istnieje wiele kluczy (klucze „jaśniejsze” zastępują klucze „lewe”), mogą łączyć wiele słowników (w razie potrzeby) i zachowują typ (z zastrzeżeniem, że wymaga znaczącego domyślnego konstruktora publicznego):
źródło
this T me
słownik został zadeklarowany za pomocąnew Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)
lub coś podobnego, wynikowy słownik nie zachowuje tego samego zachowania.me
aDictionary<K,V>
, możesz to zrobićvar newMap = new Dictionary<TKey, TValue>(me, me.Comparer);
, a także uniknąćT
parametru dodatkowego typu.Trywialnym rozwiązaniem byłoby:
źródło
Spróbuj wykonać następujące czynności
źródło
źródło
foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))
Używając go w kodzie produkcyjnym, jeśli są jakieś wady, proszę dać mi znać! :)IEnumerable<KeyValuePair<TKey, TValue>>
którym równość jest zdefiniowana przez równość klucza i wartości (istnieje przeciążenie, aby umożliwić alternatywy). Jest to dobre w przypadku pętli foreach, gdzie jest to wszystko, co jest konieczne, ale są przypadki, w których faktycznie chcesz słownika.Jestem bardzo spóźniony na imprezę i być może coś mi brakuje, ale jeśli nie ma duplikatów kluczy lub, jak mówi OP, „W przypadku kolizji nie ma znaczenia, która wartość jest zapisywana w dyktonie, o ile spójne: „co jest nie tak z tym (łączenie D2 w D1)?
Wydaje się to dość proste, może zbyt proste. Zastanawiam się, czy coś mi umknęło. Tego używam w kodzie, w którym wiem, że nie ma duplikatów kluczy. Nadal jednak testuję, więc chciałbym wiedzieć, czy coś przeoczyłem, zamiast dowiedzieć się później.
źródło
Poniższe działa dla mnie. Jeśli są duplikaty, użyje wartości dictA.
źródło
Oto funkcja pomocnika, której używam:
źródło
if(first == null)
wtedy ta logika jest bezużyteczna, ponieważfirst
nieref
jest i nie jest zwracana. I nie może tak być,ref
ponieważ jest zadeklarowany jako instancja interfejsu; musisz usunąć sprawdzanie błędów lub zadeklarować go jako instancję klasy lub po prostu zwrócić nowy słownik (bez metody rozszerzenia).Opcja 1: Zależy to od tego, co chcesz zrobić, jeśli masz pewność, że nie masz duplikatu klucza w obu słownikach. niż możesz zrobić:
Uwaga: spowoduje to wygenerowanie błędu, jeśli w słownikach pojawią się duplikaty kluczy.
Opcja 2: Jeśli możesz mieć duplikat klucza, będziesz musiał obsługiwać duplikat klucza za pomocą klauzuli where.
Uwaga: nie otrzyma duplikatu klucza. jeśli będzie jakikolwiek duplikat klucza, to dostanie on klucz Dictionary1.
Opcja 3: Jeśli chcesz użyć ToLookup. otrzymasz odnośnik, który może mieć wiele wartości na klucz. Możesz przekonwertować to wyszukiwanie na słownik:
źródło
Co powiesz na dodanie
params
przeciążenia?Powinieneś także wpisać je tak,
IDictionary
aby uzyskać maksymalną elastyczność.źródło
Biorąc pod uwagę wydajność wyszukiwania i usuwania kluczy słownikowych, ponieważ są to operacje haszujące, i biorąc pod uwagę, że sformułowanie pytania było najlepszym sposobem, myślę, że poniżej jest całkowicie poprawne podejście, a pozostałe są nieco zbyt skomplikowane, IMHO.
LUB jeśli pracujesz w aplikacji wielowątkowej, a słownik i tak musi być bezpieczny dla wątków, powinieneś to zrobić:
Następnie możesz to zawinąć, aby obsłużyć wyliczenie słowników. Niezależnie od tego, patrzysz na ~ O (3n) (wszystkie warunki są idealne), ponieważ
.Add()
zrobi to dodatkowe, niepotrzebne, ale praktycznie bezpłatne,Contains()
za kulisami. Nie sądzę, żeby było znacznie lepiej.Jeśli chcesz ograniczyć dodatkowe operacje na dużych kolekcjach, powinieneś podsumować
Count
każdy słownik, który zamierzasz scalić, i ustawić pojemność słownika docelowego do tego, co pozwala uniknąć późniejszych kosztów zmiany rozmiaru. Tak więc produkt końcowy jest taki ...Zauważ, że wybrałem
IList<T>
tę metodę ... głównie dlatego, że jeśli ją wprowadziłeśIEnumerable<T>
, otworzyłeś się na wiele wyliczeń tego samego zestawu, co może być bardzo kosztowne, jeśli masz swoją kolekcję słowników z odroczonego LINQ komunikat.źródło
W oparciu o powyższe odpowiedzi, ale dodanie parametru Func, aby umożliwić programowi wywołującemu obsługę duplikatów:
źródło
Partia jest już prawie martwa, ale tutaj jest „ulepszona” wersja user166390, która trafiła do mojej biblioteki rozszerzeń. Oprócz niektórych szczegółów dodałem delegata, aby obliczyć scaloną wartość.
źródło
@Tim: Powinien być komentarzem, ale komentarze nie pozwalają na edycję kodu.
Uwaga: zastosowałem modyfikację @ANeves do rozwiązania @Andrew Orsich, więc MergeLeft wygląda teraz tak:
źródło
Dictionary
typów. Przeciążenia można dodać dla innych typów słowników (ConcurrentDictionary
,ReadOnlyDictionary
itp.) Nie sądzę, aby utworzenie nowej listy było konieczne, a konkat jest konieczny osobiście. Właśnie iterowałem najpierw tablicę params, a następnie iterowałem każdy KVP każdego słownika. Nie widzę odwrotu.Dictionary
otrzymasz obiekt, nawet jeśli użyjesz metody rozszerzenia naConcurrentDictionary
. Może to prowadzić do trudnych do wykrycia błędów.Wiem, że to stare pytanie, ale ponieważ teraz mamy LINQ, możesz to zrobić w jednym wierszu, takim jak ten
lub
źródło
mergee
.Bałam się zobaczyć skomplikowane odpowiedzi, będąc nowym w C #.
Oto kilka prostych odpowiedzi.
Scalanie d1, d2 itd. Słowniki i obsługa nakładających się klawiszy („b” w poniższych przykładach):
Przykład 1
Przykład 2
Bardziej złożone scenariusze znajdują się w innych odpowiedziach.
Mam nadzieję, że to pomogło.
źródło
Możesz pominąć / zignorować (domyślnie) lub zastąpić duplikaty: a wujek Boba pod warunkiem, że nie jesteś zbyt wybredny co do wydajności Linq, ale wolisz zamiast tego zwięzły kod, który mogę zachować, tak jak ja: w takim przypadku możesz usunąć domyślny MergeKind.SkipDuplicates, aby wymusić wybór dla dzwoniącego i spraw, aby deweloper wiedział, jakie będą wyniki!
źródło
Zauważ, że jeśli używasz metody rozszerzenia o nazwie „Dodaj”, możesz użyć inicjatorów kolekcji, aby połączyć tyle słowników, ile potrzeba:
źródło
Scalanie przy użyciu metody rozszerzenia. Nie zgłasza wyjątku, gdy istnieją duplikaty kluczy, ale zamienia te klucze na klucze z drugiego słownika.
Stosowanie:
źródło
Uproszczony w użyciu w porównaniu z moją wcześniejszą odpowiedzią z domyślną wartością logiczną nieniszczącego scalania, jeśli istnieje lub całkowicie zastępuje, jeśli jest prawdziwy, zamiast używania wyliczenia. Nadal odpowiada moim potrzebom, nie wymagając przy tym żadnego bardziej wyszukanego kodu:
źródło
Scalanie za pomocą
EqualityComparer
mapowania elementów w celu porównania z inną wartością / typem. Tutaj zmapujemyKeyValuePair
(typ elementu przy wyliczaniu słownika) doKey
.Stosowanie:
źródło
lub:
wynikiem jest związek, w którym dla zduplikowanych wpisów wygrywa „y”.
źródło
źródło
Wersja z odpowiedzi @ user166390 z dodanym
IEqualityComparer
parametrem umożliwiającym porównanie kluczy bez rozróżniania wielkości liter.źródło
źródło