2009-03-17 10 views
7

Próbowałem napisać metodę rozszerzenia do naśladowania List.RemoveAll (Predicate).Metody rozszerzające Słownik: <TKey,TValue> .RemoveAll? Czy to możliwe?

Do tej pory mam to:

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
            Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    Dictionary<TKey,TValue> temp = new Dictionary<TKey,TValue>(); 

    foreach (var item in dict) 
    { 
     if (!condition.Invoke(item)) 
      temp.Add(item.Key, item.Value); 
    } 

    dict = temp; 
} 

Wszelkie wskazówki? Czy jest to całkowicie naiwna implementacja?

+0

Czy nie chcesz usunąć pary ze słownika, jeśli twój predykat pasuje tylko do klucza, zamiast do KeyValuePair? – base2

Odpowiedz

16

Twój kod nie będzie działał, ponieważ podajesz klasę Dictionary według wartości. Oznacza to, że ostatnie zadanie (dict = temp) nie będzie widoczne dla funkcji wywołującej. W języku C# nie jest dozwolone przekazywanie celów metod rozszerzenia przez ref lub out (w VB jest to zgodne z prawem ByRef).

Zamiast tego należy zmodyfikować wbudowany słownik. Spróbuj następującą

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
            Func<KeyValuePair<TKey,TValue>,bool> condition) 
{ 
    foreach (var cur in dict.Where(condition).ToList()) { 
     dict.Remove(cur.Key); 
    } 
} 

EDIT

zamienili kolejność Gdzie i ToList aby zmniejszyć wielkość przydzielonej pamięci listy. Teraz przydzieli tylko listę dla przedmiotów, które mają zostać usunięte.

+0

Wadą jest przydzielanie wystarczającej ilości pamięci dla listy kluczy za każdym razem. ale z pewnością proste – ShuggyCoUk

+0

Nie działa jednak ... –

+0

@Rob jak to? Działa dobrze dla przykładowych danych, których użyłem: – JaredPar

4
public static void RemoveAll<TKey,TValue>(
    this Dictionary<TKey,TValue> dict, 
    Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    var toRemove = new List<TKey>(); 

    foreach (var item in dict) 
    { 
     if (!condition(item)) 
      toRemove.Add(item); 
    } 
    foreach (var key in toRemove) 
    { 
     dict.Remove(key); 
    } 
} 

Jeżeli liczba kluczy do usunięcia jest niewielki w stosunku do wielkości słowniku będzie to szybciej (jeśli liczba usunięta może być zerowy można zrobić to nawet szybciej leniwie tworzenia listy toRemove również.

Sprowadza się to do zaktualizowanej odpowiedzi Jareda, ale pozwala odroczyć tworzenie listy usunięć, jeśli sobie tego życzysz. Jeśli to nie jest problem (i nie masz powodu, aby przełamać punkt w połowie procesu) następnie Jared's jest czystszy i prostszy

+0

Nie musisz wywoływać warunku.Nieprzypisuj (...) metody pod warunkiem, ponieważ już jest delegat. Możesz po prostu wywołać stan bezpośrednio, np. warunek (pozycja). – base2

+0

@ base2 Właśnie replikowałem styl oryginalnego użytkownika. Zgadzam się, że lepiej bez Invoke, zmienię to – ShuggyCoUk

1

Ta metoda nie działa, ponieważ parametr "dict" nie jest przekazywany przez refere nce, a tak naprawdę nie może być, ponieważ ref nie jest obsługiwany jako pierwszy parametr metody rozszerzenia.

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
           Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    var temp = new List<TKey>(); 

    foreach (var item in dict) 
    { 
     if (!condition(item)) 
      temp.Add(item.Key); 
    } 

    foreach (var itemKey in temp) 
     dict.Remove(itemKey) 
} 

Chciałbym również zobaczyć implementacje RemoveAllByKey i RemoveAllByValue.

0

Ale jeśli chcesz, możesz zwrócić nowy i inny słownik. Twój podpis zmieni się następująco:

public static Dictionary<TKey, TValue> RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
           Predicate<KeyValuePair<TKey,TValue>> condition) 

I kod wywołujący powiedziałby:

var newDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 

A jeśli chciał zmodyfikować oldDict, byłoby to nazwać tak:

oldDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 
Powiązane problemy