Condividi tramite


Operazioni set (C#)

Le operazioni set in LINQ si riferiscono alle operazioni di query che producono un set di risultati in base alla presenza o all'assenza di elementi equivalenti all'interno della stessa raccolta o di raccolte separate.

Importante

In questi esempi viene usata un'origine dati System.Collections.Generic.IEnumerable<T>. Le origini dati basate su System.Linq.IQueryProvider usano origini dati System.Linq.IQueryable<T> e alberi delle espressioni. La sintassi consentita per gli alberi delle espressioni presenta limitazioni. Inoltre, ogni origine dati IQueryProvider, ad esempio EF Core può imporre altre restrizioni. Consultare la documentazione relativa all'origine dati.

Nomi dei metodi Descrizione Sintassi dell'espressione di query C# Ulteriori informazioni
Distinct oppure DistinctBy Rimuove i valori duplicati da una Collection. Non applicabile. Enumerable.Distinct
Enumerable.DistinctBy
Queryable.Distinct
Queryable.DistinctBy
Except oppure ExceptBy Restituisce la differenza dei set, ovvero gli elementi di una raccolta che non compaiono in una seconda raccolta. Non applicabile. Enumerable.Except
Enumerable.ExceptBy
Queryable.Except
Queryable.ExceptBy
Intersect oppure IntersectBy Restituisce l'intersezione di set, ovvero gli elementi presenti in ognuna delle due Collection. Non applicabile. Enumerable.Intersect
Enumerable.IntersectBy
Queryable.Intersect
Queryable.IntersectBy
Union oppure UnionBy Restituisce l'unione di set, ovvero gli elementi univoci presenti in una delle due Collection. Non applicabile. Enumerable.Union
Enumerable.UnionBy
Queryable.Union
Queryable.UnionBy

Distinct e DistinctBy

L'esempio seguente mostra il comportamento del metodo Enumerable.Distinct su una sequenza di stringhe. La sequenza restituita contiene gli elementi univoci dalla sequenza di input.

Elemento grafico che illustra il comportamento di Distinct()

string[] words = ["the", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"];

IEnumerable<string> query = from word in words.Distinct()
                            select word;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * the
 * quick
 * brown
 * fox
 * jumped
 * over
 * lazy
 * dog
 */

DistinctBy è un approccio alternativo a Distinct che accetta un keySelector. keySelector viene utilizzato come discriminante comparativo del tipo di origine. Nel seguente codice, le parole vengono discriminate in base al relativo Lengthe viene visualizzata la prima parola di ogni lunghezza:

string[] words = ["the", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"];

foreach (string word in words.DistinctBy(p => p.Length))
{
    Console.WriteLine(word);
}

// This code produces the following output:
//     the
//     quick
//     jumped
//     over

Except e ExceptBy

L'esempio seguente mostra il comportamento di Enumerable.Except. La sequenza restituita contiene solo gli elementi della prima sequenza di input che non sono presenti nella seconda sequenza di input.

Elemento grafico che illustra l'azione di Except()

Nota

Gli esempi seguenti in questo articolo usano le origini dati comuni per questa area.
Ogni Student ha un livello di classe, un reparto primario e una serie di punteggi. Un Teacher ha anche una proprietà City che identifica il campus in cui l'insegnante tiene le lezioni. Un Department ha un nome e un riferimento a un Teacher che funge da responsabile del reparto.
È possibile trovare il set di dati di esempio nel repository di origine.

public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required int ID { get; init; }

    public required GradeLevel Year { get; init; }
    public required List<int> Scores { get; init; }

    public required int DepartmentID { get; init; }
}

public class Teacher
{
    public required string First { get; init; }
    public required string Last { get; init; }
    public required int ID { get; init; }
    public required string City { get; init; }
}

public class Department
{
    public required string Name { get; init; }
    public int ID { get; init; }

    public required int TeacherID { get; init; }
}
string[] words1 = ["the", "quick", "brown", "fox"];
string[] words2 = ["jumped", "over", "the", "lazy", "dog"];

IEnumerable<string> query = from word in words1.Except(words2)
                            select word;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * quick
 * brown
 * fox
 */

Il metodo ExceptBy è un approccio alternativo a Except che accetta due sequenze di tipi possibilmente eterogenei e un keySelector. keySelector è lo stesso tipo di quello della prima raccolta. Si consideri il seguente array Teacher e gli ID insegnante da escludere. Per trovare insegnanti della prima raccolta che non sono presenti nella seconda raccolta, è possibile proiettare l'ID dell'insegnante sulla seconda raccolta:

int[] teachersToExclude =
[
    901,    // English
    965,    // Mathematics
    932,    // Engineering
    945,    // Economics
    987,    // Physics
    901     // Chemistry
];

foreach (Teacher teacher in
    teachers.ExceptBy(
        teachersToExclude, teacher => teacher.ID))
{
    Console.WriteLine($"{teacher.First} {teacher.Last}");
}

Nel codice C# precedente:

  • L'array teachers viene filtrato solo per gli insegnanti che non sono presenti nell'array teachersToExclude.
  • L'array teachersToExclude contiene il valore ID per tutti i responsabili del reparto.
  • La chiamata a ExceptBy produce un nuovo set di valori che vengono scritti nella console.

Il nuovo set di valori è di tipo Teacher, che è il tipo della prima raccolta. Ogni teacher nell'array teachers che non ha un valore ID corrispondente nell'array teachersToExclude viene scritto nella console.

Intersect e IntersectBy

L'esempio seguente mostra il comportamento di Enumerable.Intersect. La sequenza restituita contiene gli elementi comuni a entrambe le sequenze di input.

Elemento grafico che illustra l'intersezione di due sequenze

string[] words1 = ["the", "quick", "brown", "fox"];
string[] words2 = ["jumped", "over", "the", "lazy", "dog"];

IEnumerable<string> query = from word in words1.Intersect(words2)
                            select word;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * the
 */

Il metodo IntersectBy è un approccio alternativo a Intersect che accetta due sequenze di tipi possibilmente eterogenei e un keySelector. keySelector viene utilizzato come discriminante comparativo del tipo della seconda raccolta. Si considerino i seguenti array di studenti e insegnanti. La query corrisponde agli elementi in ogni sequenza in base al nome per trovare gli studenti che sono anche insegnanti:

foreach (Student person in
    students.IntersectBy(
        teachers.Select(t => (t.First, t.Last)), s => (s.FirstName, s.LastName)))
{
    Console.WriteLine($"{person.FirstName} {person.LastName}");
}

Nel codice C# precedente:

  • La query produce l'intersezione di Teacher e Student confrontando i nomi.
  • Solo le persone che si trovano in entrambi gli array sono presenti nella sequenza risultante.
  • Le istanze risultanti Student vengono scritte nella console.

Union e UnionBy

L'esempio seguente mostra un'operazione di unione su due sequenze di stringhe. La sequenza restituita contiene gli elementi univoci da entrambe le sequenze di input.

Grafica che mostra l'unione di due sequenze

string[] words1 = ["the", "quick", "brown", "fox"];
string[] words2 = ["jumped", "over", "the", "lazy", "dog"];

IEnumerable<string> query = from word in words1.Union(words2)
                            select word;

foreach (var str in query)
{
    Console.WriteLine(str);
}

/* This code produces the following output:
 *
 * the
 * quick
 * brown
 * fox
 * jumped
 * over
 * lazy
 * dog
*/

Il metodo UnionBy è un approccio alternativo a Union che accetta due sequenze dello stesso tipo e un keySelector. keySelector viene utilizzato come discriminante comparativo del tipo di origine. La query seguente produce l'elenco di tutte le persone che sono studenti o insegnanti. Gli studenti che sono anche insegnanti vengono aggiunti al set di unione una sola volta:

foreach (var person in
    students.Select(s => (s.FirstName, s.LastName)).UnionBy(
        teachers.Select(t => (FirstName: t.First, LastName: t.Last)), s => (s.FirstName, s.LastName)))
{
    Console.WriteLine($"{person.FirstName} {person.LastName}");
}

Nel codice C# precedente:

  • Gli array teachers e studentsvengono uniti insieme usando i loro nomi come selettore a chiave.
  • I nomi risultanti vengono scritti nella console.

Vedi anche