Domanda

Sto cercando un modo molto veloce per filtrare una raccolta in C#.Attualmente sto utilizzando raccolte List<object> generiche, ma sono disponibile a utilizzare altre strutture se funzionano meglio.

Attualmente sto solo creando un nuovo List<object> e sto eseguendo il looping dell'elenco originale.Se i criteri di filtro corrispondono, ne inserisco una copia nel nuovo elenco.

Esiste un modo migliore per farlo?Esiste un modo per filtrare in modo che non sia richiesto un elenco temporaneo?

È stato utile?

Soluzione

Se usi C# 3.0 puoi usare linq, molto meglio e molto più 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();

Se non riesci a trovare il .Where, ciò significa che devi importare using System.Linq; nella parte superiore del file.

Altri suggerimenti

Ecco un blocco di codice/esempio di alcuni filtri di elenchi che utilizzano tre metodi diversi che ho messo insieme per mostrare il filtraggio di elenchi basato su Lambda e 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

List ha il metodo FindAll che eseguirà il filtraggio per te e restituirà un sottoinsieme dell'elenco.

Il msdn ha un ottimo esempio di codice qui: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

MODIFICARE:L'ho scritto prima di avere una buona conoscenza di Linq e del metodo Where().Se dovessi scrivere questo oggi probabilmente utilizzerei il metodo menzionato da Jorge sopra.Il metodo FindAll funziona ancora se sei bloccato in un ambiente .NET 2.0.

È possibile utilizzare IEnumerable per eliminare la necessità di un elenco temporaneo.

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

dove Matches è il nome del tuo metodo di filtro.E puoi usarlo come:

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

Ciò chiamerà la funzione GetFilteredItems quando necessario e in alcuni casi in cui non si utilizzano tutti gli elementi nella raccolta filtrata, potrebbe fornire un buon miglioramento delle prestazioni.

Puoi usare il Trova tutto del List, fornendo un delegato su cui filtrare.Comunque sono d'accordo con @IainMH che non vale la pena preoccuparsi troppo a meno che non si tratti di un elenco enorme.

Per farlo sul posto, puoi utilizzare il metodo RemoveAll della classe "List<>" insieme a una classe "Predicate" personalizzata... ma tutto ciò che fa è ripulire il codice...sotto il cofano sta facendo la stessa cosa che fai tu... ma sì, lo fa sul posto, quindi fai lo stesso con l'elenco temporaneo.

L'uso di Linq è relativamente più lento rispetto all'uso di un predicato fornito al metodo Lists FindAll.Bisogna anche fare attenzione con Linq poiché l'eumerazione dell'elenco non viene effettivamente eseguita finché non si accede al risultato.Ciò può significare che quando pensi di aver creato un elenco filtrato, il contenuto potrebbe differire da quello che ti aspettavi quando lo hai effettivamente letto.

Se usi C# 3.0 puoi usare linq

Oppure, se preferisci, utilizza la sintassi speciale della query fornita dal compilatore C# 3:

var filteredList = from x in myList
                   where x > 7
                   select x;

Se il tuo elenco è molto grande e stai filtrando ripetutamente, puoi ordinare l'elenco originale in base all'attributo filtro, ricerca binaria per trovare i punti iniziale e finale.

Tempo iniziale O(n*log(n)) quindi O(log(n)).

Il filtraggio standard richiederà O(n) ogni volta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top