Filtrar colecciones en C#


Estoy buscando una forma muy rápida de filtrar una colección en C#. Actualmente estoy usando colecciones genéricas de List, pero estoy abierto a usar otras estructuras si funcionan mejor.

Actualmente, estoy creando una nueva Lista y haciendo un bucle a través de la lista original. Si los criterios de filtrado coinciden, pongo una copia en la nueva lista.

¿Hay una mejor manera de hacer esto? ¿Hay alguna forma de filtrar en su lugar para que no se requiera una lista temporal?

Author: Jason Z, 2008-08-25

9 answers

Si estás usando C # 3.0 puedes usar linq, mucho mejor y mucho más elegante:

List<int> myList = GetListOfIntsFromSomewhere();

// This will filter out the list of ints that are > than 7, Where returns an
// IEnumerable<T> so a call to ToList is required to convert back to a List<T>.
List<int> filteredList = myList.Where( x => x > 7).ToList();
 170
Author: Jorge Córdoba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-16 18:10:41

Aquí hay un bloque de código / ejemplo de un filtrado de listas usando tres métodos diferentes que he reunido para mostrar el filtrado de listas basado en Lambdas y LINQ.

#region List Filtering

static void Main(string[] args)
{
    ListFiltering();
    Console.ReadLine();
}

private static void ListFiltering()
{
    var PersonList = new List<Person>();

    PersonList.Add(new Person() { Age = 23, Name = "Jon", Gender = "M" }); //Non-Constructor Object Property Initialization
    PersonList.Add(new Person() { Age = 24, Name = "Jack", Gender = "M" });
    PersonList.Add(new Person() { Age = 29, Name = "Billy", Gender = "M" });

    PersonList.Add(new Person() { Age = 33, Name = "Bob", Gender = "M" });
    PersonList.Add(new Person() { Age = 45, Name = "Frank", Gender = "M" });

    PersonList.Add(new Person() { Age = 24, Name = "Anna", Gender = "F" });
    PersonList.Add(new Person() { Age = 29, Name = "Sue", Gender = "F" });
    PersonList.Add(new Person() { Age = 35, Name = "Sally", Gender = "F" });
    PersonList.Add(new Person() { Age = 36, Name = "Jane", Gender = "F" });
    PersonList.Add(new Person() { Age = 42, Name = "Jill", Gender = "F" });

    //Logic: Show me all males that are less than 30 years old.

    Console.WriteLine("");
    //Iterative Method
    Console.WriteLine("List Filter Normal Way:");
    foreach (var p in PersonList)
        if (p.Gender == "M" && p.Age < 30)
            Console.WriteLine(p.Name + " is " + p.Age);

    Console.WriteLine("");
    //Lambda Filter Method
    Console.WriteLine("List Filter Lambda Way");
    foreach (var p in PersonList.Where(p => (p.Gender == "M" && p.Age < 30))) //.Where is an extension method
        Console.WriteLine(p.Name + " is " + p.Age);

    Console.WriteLine("");
    //LINQ Query Method
    Console.WriteLine("List Filter LINQ Way:");
    foreach (var v in from p in PersonList
                      where p.Gender == "M" && p.Age < 30
                      select new { p.Name, p.Age })
        Console.WriteLine(v.Name + " is " + v.Age);
}

private class Person
{
    public Person() { }
    public int Age { get; set; }
    public string Name { get; set; }
    public string Gender { get; set; }
}

#endregion
 21
Author: Jon Erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-30 07:31:39

List tiene el método findAll que hará el filtrado por usted y devolverá un subconjunto de la lista.

El msdn tiene un gran ejemplo de código aquí: http://msdn.microsoft.com/en-us/library/aa701359 (VS.80).aspx

EDITAR: Escribí esto antes de tener un buen entendimiento de Linq y el método Where (). Si tuviera que escribir esto hoy, probablemente usaría el método que Jorge menciona anteriormente. Sin embargo, el método findAll todavía funciona si está atascado en un entorno.NET 2.0.

 9
Author: Mykroft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-15 17:48:03

Puede usarerableumerable para eliminar la necesidad de una lista temporal.

public IEnumerable<T> GetFilteredItems(IEnumerable<T> collection)
{
    foreach (T item in collection)
    if (Matches<T>(item))
    {
        yield return item;
    }
}

Donde Coincidencias es el nombre de su método de filtro. Y puedes usar esto como:

IEnumerable<MyType> filteredItems = GetFilteredItems(myList);
foreach (MyType item in filteredItems)
{
    // do sth with your filtered items
}

Esto llamará a la función GetFilteredItems cuando sea necesario y en algunos casos que no utilice todos los elementos de la colección filtrada, puede proporcionar una buena ganancia de rendimiento.

 6
Author: Serhat Ozgel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-25 15:22:33

Para hacerlo en su lugar, puede usar el método removeAll de la clase "List" junto con una clase "Predicate" personalizada...pero todo lo que hace es limpiar el código... bajo el capó está haciendo lo mismo que tú...pero sí, lo hace en su lugar, por lo que hacer lo mismo la lista de temporeros.

 3
Author: Adam Haile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-25 15:15:41

Puede usar el método findAll de la Lista, proporcionando un delegado para filtrar. Sin embargo, estoy de acuerdo con @IainMH que no vale la pena preocuparse demasiado a menos que sea una lista enorme.

 2
Author: bdukes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:13

Usar Linq es relativamente más lento que usar un predicado suministrado al método Lists findAll. También hay que tener cuidado con Linq ya que la eumeración de la lista no se ejecuta realmente hasta que se accede al resultado. Esto puede significar que cuando usted piensa que ha creado una lista filtrada, el contenido puede diferir de lo que esperaba cuando realmente lo leyó.

 2
Author: gouldos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-12 15:04:15

Si estás usando C # 3.0 puedes usar linq

O, si lo prefiere, utilice la sintaxis de consulta especial proporcionada por el compilador de C# 3:

var filteredList = from x in myList
                   where x > 7
                   select x;
 1
Author: Tom Lokhorst,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-25 15:23:04

Si su lista es muy grande y está filtrando repetidamente, puede ordenar la lista original en el atributo filtro, búsqueda binaria para encontrar los puntos de inicio y final.

Tiempo inicial O(n*log(n)) luego O(log(n)).

El filtrado estándar tomará O(n) cada vez.

 0
Author: Daniel Roberts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-30 11:21:53