Pregunta

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

Actualmente, solo estoy creando una nueva Lista <objeto> y recorriendo la lista original.Si los criterios de filtrado coinciden, coloco una copia en la nueva lista.

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

¿Fue útil?

Solución

Si estás usando C# 3.0, puedes usar linq, mucho mejor y 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();

Si no puedes encontrar el .Where, eso significa que necesitas importar using System.Linq; en la parte superior de su archivo.

Otros consejos

Aquí hay un bloque de código/ejemplo de un filtrado de listas utilizando tres métodos diferentes que preparé 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

La lista tiene el método FindAll que filtrará 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 conocimiento 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ás atrapado en un entorno .NET 2.0.

Puede utilizar IEnumerable 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, si no utiliza todos los elementos de la colección filtrada, puede proporcionar una buena ganancia de rendimiento.

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

Para hacerlo en su lugar, puede usar el método RemoveAll de la clase "List<>" junto con una clase personalizada "Predicate"... pero todo lo que hace es limpiar el código...Debajo del capó está haciendo lo mismo que tú... pero sí, lo hace en el lugar, por lo que haces lo mismo con la lista temporal.

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

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;

Si su lista es muy grande y está filtrando repetidamente, puede ordenar la lista original según el atributo de filtro y realizar una búsqueda binaria para encontrar los puntos de inicio y finalización.

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top