Operaciones set [C#]

Las operaciones Set de LINQ se refieren a operaciones de consulta que generan un conjunto de resultados en función de la presencia o ausencia de elementos equivalentes dentro de la misma colección o en distintas colecciones.

Nombres de método Descripción Sintaxis de la expresión de consulta de C# Más información
Distinct o DistinctBy Quita valores duplicados de una colección. No aplicable. Enumerable.Distinct
Enumerable.DistinctBy
Queryable.Distinct
Queryable.DistinctBy
Except o ExceptBy Devuelve la diferencia de conjuntos, es decir, los elementos de una colección que no aparecen en una segunda colección. No aplicable. Enumerable.Except
Enumerable.ExceptBy
Queryable.Except
Queryable.ExceptBy
Intersect o IntersectBy Devuelve la intersección de conjuntos, es decir, los elementos que aparecen en las dos colecciones. No aplicable. Enumerable.Intersect
Enumerable.IntersectBy
Queryable.Intersect
Queryable.IntersectBy
Union o UnionBy Devuelve la unión de conjuntos, es decir, los elementos únicos que aparecen en una de las dos colecciones. No es aplicable. Enumerable.Union
Enumerable.UnionBy
Queryable.Union
Queryable.UnionBy

Distinct y DistinctBy

En la siguiente ilustración se muestra el comportamiento del método Enumerable.Distinct en una secuencia de cadenas. La secuencia devuelta contiene los elementos únicos de la secuencia de entrada.

Gráfico que muestra el comportamiento de 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 es un enfoque alternativo a Distinct, que adopta keySelector. keySelector se usa como discriminador comparativo del tipo de origen. En el siguiente código, las palabras se discriminan en función de su Length, y se muestra la primera palabra de cada longitud:

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 y ExceptBy

En el ejemplo siguiente se muestra el comportamiento de Enumerable.Except. La secuencia devuelta solo contiene los elementos de la primera secuencia de entrada que no están en la segunda secuencia de entrada.

Gráfico que muestra la acción de Except()

En los ejemplos siguientes de este artículo se usan los orígenes de datos comunes para esta área:

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; }
}

Cada Student tiene un nivel académico, un departamento principal y una serie de puntuaciones. Un Teacher también tiene una propiedad City que identifica el campus donde el profesor imparte clases. Un Department tiene un nombre y una referencia a un Teacher que actúa como jefe del departamento.

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
 */

El método ExceptBy es un enfoque alternativo a Except que adopta dos secuencias de tipos posiblemente heterogéneos y keySelector. El keySelector es el mismo tipo que el tipo de la primera colección. Tenga en cuenta la siguiente matriz Teacher e identificadores de profesor que se van a excluir. Para buscar profesores en la primera colección que no están en la segunda, puede proyectar el identificador de profesor en la segunda colección:

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}");
}

En el código de C# anterior:

  • La matriz teachers se filtra solo a los profesores que no están en la matriz teachersToExclude.
  • La matriz teachersToExclude contiene el valor ID de todos los jefes de departamento.
  • La llamada a ExceptBy da como resultado un nuevo conjunto de valores que se escriben en la consola.

El nuevo conjunto de valores es de tipo Teacher, que es el tipo de la primera colección. Cada teacher de la matriz teachers que no tiene un valor de identificador correspondiente en la matriz teachersToExclude se escribe en la consola.

Intersect y IntersectBy

En el ejemplo siguiente se muestra el comportamiento de Enumerable.Intersect. La secuencia devuelta contiene los elementos que son comunes a las dos secuencias de entrada.

Gráfico que muestra la intersección de dos secuencias

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
 */

El método IntersectBy es un enfoque alternativo a Intersect que adopta dos secuencias de tipos posiblemente heterogéneos y keySelector. keySelector se usa como discriminador comparativo del tipo de la segunda colección. Tenga en cuenta las siguientes matrices de estudiantes y profesores. La consulta coincide con los elementos de cada secuencia por nombre para encontrar a los estudiantes que no son profesores también:

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

En el código de C# anterior:

  • La consulta genera la intersección de Teacher y Student comparando nombres.
  • Solo las personas que se encuentran en ambas matrices están presentes en la secuencia resultante.
  • Las instancias Student resultantes se escriben en la consola.

Union y UnionBy

En el siguiente ejemplo se muestra una operación de unión en dos secuencias de cadenas. La secuencia devuelta contiene los elementos únicos de las dos secuencias de entrada.

Gráfico en el que se muestra la unión de dos secuencias.

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
*/

UnionBy es un enfoque alternativo a Union que adopta dos secuencias del mismo tipo y keySelector. keySelector se usa como discriminador comparativo del tipo de origen. La siguiente consulta genera la lista de todas las personas que son estudiantes o profesores. Los estudiantes que también son profesores se agregan a la unión establecida una sola vez:

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}");
}

En el código de C# anterior:

  • Las matrices teachers y students se entrelazan con sus nombres como selector de claves.
  • Los nombres resultantes se escriben en la consola.

Consulte también