2009-03-17 14 views
7

Ho cercato di scrivere un metodo di estensione per simulare List.RemoveAll (Predicato).Metodi di estensione Dizionario <TKey,TValue>. Rimuovi tutto? È possibile?

Finora ho questo:

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

Tutti gli indicatori? È un'implementazione completamente ingenua?

+0

Non vorresti eliminare le coppie dal dizionario facendo coincidere la tua corrispondenza di predicato con Key, invece che con KeyValuePair? – base2

risposta

16

Il tuo codice non funzionerà perché stai passando la classe Dizionario per valore. Ciò significa che l'assegnazione finale (dict = temp) non sarà visibile a una funzione di chiamata. Non è legale in C# passare i target dei metodi di estensione per ref o out (in VB è legale fare ByRef).

Invece è necessario modificare il dizionario in linea. Provare il seguente

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

Scambiato dell'ordine di Dove e ToList per ridurre la dimensione della memoria allocata dell'elenco. Ora assegnerà solo un elenco per gli elementi che devono essere rimossi.

+0

Ha lo svantaggio di allocare memoria sufficiente per l'elenco delle chiavi ogni volta. ma certamente semplice – ShuggyCoUk

+0

In realtà non funziona però ... –

+0

@Rob come mai? Funziona bene per i dati di esempio che ho usato – 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); 
    } 
} 

Se il numero di chiavi per rimuovere è piccolo rispetto alle dimensioni dizionario questa sarà più veloce (se il numero rimossa è probabile che sia pari a zero è possibile rendere questo ancora più velocemente creando pigramente la lista toremove pure.

Si riduce allo stesso modo della risposta aggiornata di Jared ma consente di posticipare la creazione della lista di rimozione se lo si desidera.Se questo non è un problema (e non si ha motivo di interromperlo in qualche modo attraverso il processo quindi Jared's è più pulito e più semplice

+0

Non è necessario chiamare la condizione. Invocare il metodo (...) sulla condizione, perché, è già un delegare. Puoi semplicemente chiamare la condizione direttamente, ad es. condizione (articolo). – base2

+0

@ base2 Stavo solo replicando lo stile degli utenti originali. Sono d'accordo che è meglio senza Invoke, lo cambierò – ShuggyCoUk

1

Questo metodo non funziona perché il parametro "dict" non viene passato da refere nce, e infatti non può essere perché ref non è supportato come primo parametro di un metodo di estensione.

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

Mi piacerebbe vedere RemoveAllByKey e RemoveAllByValue implementazioni pure.

0

Ma se si desidera, è possibile restituire un nuovo e diverso dizionario. La tua firma cambierebbe a questo:

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

E il codice chiamante direbbe:

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

E, se si voleva modificare oldDict, si dovrebbe chiamare in questo modo:

oldDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 
Problemi correlati