cómo eliminar cadenas vacías de la lista, luego eliminar valores duplicados de una lista


81

Digamos que tengo una lista de algunos valores de columna que provienen de una tabla, ¿cómo elimino cadenas vacías y valores duplicados? Consulte el siguiente código:

List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();

Esto es lo que acabo de codificar, pero el código de Amiram es mucho más elegante, así que elegiré esa respuesta así es como lo hice:

DataTable dtReportsList = someclass.GetReportsList();

        if (dtReportsList.Rows.Count > 0)
       { 
           List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();
           dtList.RemoveAll(x=>x == "");
           dtList = dtList.Distinct().ToList();         

           rcboModule.DataSource = dtList;
           rcboModule.DataBind();               
           rcboModule.Items.Insert(0, new RadComboBoxItem("All", "All"));
       }

Comprenda que RemoveAll () muta dtList; cada elemento que se elimina obliga a la Lista a reorganizar los elementos en índices más altos en la matriz subyacente que utiliza. Sería más rápido simplemente omitirlos como lo hace Amiram con su método Where.
KeithS

Respuestas:


200
dtList  = dtList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList()

Supuse que la cadena vacía y los espacios en blanco son como nulos. Si no, puede usar IsNullOrEmpty(permitir espacios en blanco), os != null


Sólo una cosa; deducir con Distinct () es relativamente ineficaz porque el método debe asumir el peor de los casos.
KeithS

@KeithS ¿Qué afirmaciones conocemos sobre estos datos que Distinctno lo hagan y que permitan optimizarlos?
Servicio

Podemos ordenar la lista y luego afirmar que está ordenada, haciendo que el algoritmo de deduplicación sea lineal; mira mi respuesta.
KeithS

9

La respuesta de Amiram es correcta, pero Distinct () tal como se implementó es una operación N 2 ; para cada elemento de la lista, el algoritmo lo compara con todos los elementos ya procesados ​​y lo devuelve si es único o lo ignora en caso contrario. Podemos hacerlo mejor.

Una lista ordenada se puede deducir en tiempo lineal; si el elemento actual es igual al elemento anterior, ignórelo; de lo contrario, devuélvalo. La clasificación es NlogN, por lo que incluso teniendo que ordenar la colección, obtenemos algún beneficio:

public static IEnumerable<T> SortAndDedupe<T>(this IEnumerable<T> input)
{
   var toDedupe = input.OrderBy(x=>x);

   T prev;
   foreach(var element in toDedupe)
   {
      if(element == prev) continue;

      yield return element;
      prev = element;      
   }
}

//Usage
dtList  = dtList.Where(s => !string.IsNullOrWhitespace(s)).SortAndDedupe().ToList();

Esto devuelve los mismos elementos; simplemente están ordenados.


Excelente. Si no me equivoco, al iterar los elementos, en realidad está realizando el pedido. ¿Puedes pensar en una manera de hacer que tu método sea "perezoso"?
Amiram Korach

Desafortunadamente, la mayoría de los tipos requieren el conocimiento de toda la colección para ser ordenados; el último elemento podría ser el primero que deba devolverse. Por lo tanto, todos los elementos de la entrada deben evaluarse para producir el primer elemento de la salida. El único tipo en el que puedo pensar que podría interrumpirse después de encontrar el siguiente elemento de su salida es una variante SelectionSort, y en ese caso volvemos al punto de partida.
KeithS

Además, en nuestro caso, el resultado de toda la operación es una lista, que requiere una ejecución "ansiosa" para empezar. Si quisiéramos trabajar con él como un IEnumerable y diferir su ejecución, podría tomar el meollo de la función y ponerlo en una clase Iterator oculta que implemente IEnumerable.
KeithS

Distinctusa hash y debería estar más cerca de O (N) que de O (N ^ 2). fuente
Risky Martin

... Bueno, estaré maldito, de hecho lo hace; System.Linq.Set es una implementación de tabla hash interna utilizada por Distinct, que estaría cerca del tiempo de acceso O (1) asumiendo que la implementación GetHashCode () de sus elementos es eficiente y produce un hash distribuido uniformemente (la implementación predeterminada lo haría) . Sin embargo, una tabla hash tiene problemas de memoria; La implementación básica de .NET utiliza dos matrices, una de ints y otra de elementos vinculados, cada una en el mejor de los casos igual al número de elementos del conjunto y en el peor el doble.
KeithS

1

La solución de Amiram Korach es realmente ordenada. Aquí hay una alternativa en aras de la versatilidad.

var count = dtList.Count;
// Perform a reverse tracking.
for (var i = count - 1; i > -1; i--)
{
    if (dtList[i]==string.Empty) dtList.RemoveAt(i);
}
// Keep only the unique list items.
dtList = dtList.Distinct().ToList();

4
Si bien esto funcionaría, la cláusula Where es más rápida porque no tiene que mutar la colección de entrada. Está minimizando el número de "turnos" que se deben realizar al eliminar elementos de la lista, pero Where no elimina nada de la entrada; simplemente omite los elementos que no coinciden.
KeithS

0

Para simplificar la solución de Amiram Korach :

dtList.RemoveAll(s => string.IsNullOrWhiteSpace(s))

No es necesario utilizar Distinct () o ToList ()

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.