2009-03-17 9 views
7

He estado tratando de escribir un método de extensión para imitar List.RemoveAll (Predicate).Métodos de extensión Dictionary <TKey,TValue> .RemoveAll? ¿Es posible?

Hasta ahora lo he entendido:

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

Cualquier punteros? ¿Es esto una implementación completamente ingenua?

+0

¿No le gustaría eliminar pares del diccionario haciendo que su predicado coincida solo con Key, en lugar de KeyValuePair? – base2

Respuesta

16

Su código no funcionará porque está pasando la clase de diccionario por valor. Esto significa que la asignación final (dict = temp) no será visible para una función de llamada. No es legal en C# aprobar los objetivos del método de extensión por ref o out (en VB es legal hacer ByRef).

En su lugar, tendrá que modificar el diccionario en línea. Pruebe lo siguiente

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

EDITAR

cambiado el orden de las Dónde y ToList para reducir el tamaño de la memoria asignada de la lista. Ahora solo asignará una lista para los artículos que se eliminarán.

+0

Tiene la desventaja de asignar suficiente memoria para la lista de claves cada vez. pero ciertamente simple – ShuggyCoUk

+0

en realidad no funciona ... –

+0

@Rob ¿cómo es eso? Funciona bien para los datos de muestra que he usado – 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); 
    } 
} 

Si el número de llaves para retirar es pequeño en relación al tamaño del diccionario esta será más rápido (si es probable que sea cero el número eliminado se puede hacer esto aún más rápido mediante la creación perezosamente la lista toremove también.

Esto se reduce a lo mismo que la respuesta actualizada de Jared, pero le permite diferir la creación de la lista de eliminación si así lo desea. Si esto no es un problema (y no tiene motivo para romper el punto en parte) entonces Jared es más limpio y más simple.

+0

No es necesario llamar al método condition.Invoke (...) con la condición, porque ya es un delegar. Puede simplemente llamar a la condición directamente, p. condición (elemento). – base2

+0

@ base2 Solo estaba replicando el estilo original de los usuarios. Estoy de acuerdo que es mejor sin la invocación, lo cambiaré – ShuggyCoUk

1

Ese método no funcionará porque el parámetro "dict" no se pasa por refere nce, y de hecho no puede ser porque ref no es compatible como el primer parámetro de un método de extensión.

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

me gustaría ver RemoveAllByKey y RemoveAllByValue implementaciones también.

0

Pero si quisiera, podría devolver un diccionario nuevo y diferente. Su firma cambiaría a esto:

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

Y el código de llamada diría:

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

Y, si desea modificar oldDict, que podríamos llamar de esta manera:

oldDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 
Cuestiones relacionadas