LINQ - Apresentando os métodos IntercepBy e UnionBy
 Hoje vou apresentar os novos métodos InterceptBy e UnionBy da LINQ para operar com conjuntos disponíveis a partir do .NET 6.

Continuando o artigo anterior vamos apresentar os novos métodos InterceptBy e UnionBy.

Esses métodos realizam operações em conjuntos em comparação com outros conjuntos de valores e podem ser definidos assim:

Eles possuem o mesmo objetivo dos métodos anteriores com a diferença de que operam em objetos complexos e que  eles operam com uma chave de seleção(KeySelector) usada como um discriminador comparativo para realizar as respectivas operações.

Usando o método InterceptBy

Este método aceita duas coleções e gera uma nova coleção somente com os itens em comum com base em uma chave de seleção ou critério usado para fazer a interseção.

Vamos criar um projeto Console (.NET Core) no NET 6 chamado LINQ_InterceptBy.

No projeto criado vamos abrir a classe Program e definir um tipo record Usuario:

public record Usuario
{
    public string? Nome { get; init; }
    public DateTime Nascimento { get; init; }
}
 

A seguir vamos criar uma lista de usuários chamada usuarios1:

var usuarios1 = new[]
{
    new Usuario() { Nome = "Marta", Nascimento = new DateTime(1985, 12, 6) },
    new Usuario() { Nome = "Ricardo", Nascimento = new DateTime(1984, 8, 22) },
    new Usuario() { Nome = "Amanda", Nascimento = new DateTime(2001, 7, 29) },
    new Usuario() { Nome = "Jaime", Nascimento = new DateTime(2004, 1, 16) },
};

Vamos criar outra lista de usuários chamada usuarios2:

var usuarios2 = new Usuario[]
{
     new () { Nome = "Felício", Nascimento = new DateTime(2001, 6, 1)},
     new () { Nome = "Soraia", Nascimento = new DateTime(2004, 1, 16) },
     new () { Nome = "Salvador", Nascimento = new DateTime(1975, 10, 11) },
     new () { Nome = "Benedita", Nascimento = new DateTime(1966, 9, 30) },
     new () { Nome = "Silvia", Nascimento = new DateTime(1984, 3, 17) },
};

Nosso objetivo é obter a interseção por ano de nascimento entre as duas listas usando o método InterceptBy. Para isso usamos a seguinte consulta:

var PorAnoNascimento = usuarios1.IntersectBy(usuarios2.Select(x => x.Nascimento.Year), x => x.Nascimento.Year);

Assim desejamos obter os usuários contidos em usuarios1 cujo ano de nascimento seja comum com os usuários em usuarios2, e,  para isso aplicamos o método InterceptBy em usuarios1 e a seguir definimos a consulta para obter os anos da segunda coleção. A seguir definimos o critério ou chave de seleção que será usada para interceptar os elementos comuns das duas coleções.

O código completo do projeto é dado a seguir:

var usuarios1 = new[]
{
    new Usuario() { Nome = "Marta", Nascimento = new DateTime(1985, 12, 6) },
    new Usuario() { Nome = "Ricardo", Nascimento = new DateTime(1984, 8, 22) },
    new Usuario() { Nome = "Amanda", Nascimento = new DateTime(2001, 7, 29) },
    new Usuario() { Nome = "Jaime", Nascimento = new DateTime(2004, 1, 16) },
};

var usuarios2 = new Usuario[]
{
     new () { Nome = "Felício", Nascimento = new DateTime(2001, 6, 1)},
     new () { Nome = "Soraia", Nascimento = new DateTime(2004, 1, 16) },
     new () { Nome = "Salvador", Nascimento = new DateTime(1975, 10, 11) },
     new () { Nome = "Benedita", Nascimento = new DateTime(1966, 9, 30) },
     new () { Nome = "Silvia", Nascimento = new DateTime(1984, 3, 17) },
};

Console.WriteLine("\n### Usando InterceptBy ###\n");

var intersecaoPorAnoNascimento = usuarios1.IntersectBy(usuarios2.Select(x =>
                                                  x.Nascimento.Year), x => x.Nascimento.Year)
                                                  .OrderBy(x=> x.Nascimento.Year);

foreach(var usuario in intersecaoPorAnoNascimento)
    Console.WriteLine($"\t{usuario.Nome} - {usuario.Nascimento.Year}");

Console.ReadKey();

public record Usuario
{
    public string? Nome { get; init; }
    public DateTime Nascimento { get; init; }
}

No código acima estamos ordenando o resultado pelo ano de nascimento.

Executando o projeto teremos:

Usando o método UnionBy

Este método vai combinar duas coleções gerando uma nova coleção com base em uma chave de seleção ou critério usado para unificar as coleções.

Vamos criar outro projeto Console (.NET Core) no NET 6 chamado LINQ_UnionBy.

No projeto criado vamos usar o mesmo código do exemplo anterior criando um record Usuario e duas listas usuarios1 e usuarios2:

public record Usuario
{
    public string? Nome { get; init; }
    public DateTime Nascimento { get; init; }
}
 

Lista de usuarios1:

var usuarios1 = new[]
{
    new Usuario() { Nome = "Marta", Nascimento = new DateTime(1985, 12, 6) },
    new Usuario() { Nome = "Ricardo", Nascimento = new DateTime(1984, 8, 22) },
    new Usuario() { Nome = "Amanda", Nascimento = new DateTime(2001, 7, 29) },
    new Usuario() { Nome = "Jaime", Nascimento = new DateTime(2004, 1, 16) },
};

Lista de usuarios2:

var usuarios2 = new Usuario[]
{
     new () { Nome = "Felício", Nascimento = new DateTime(2001, 6, 1)},
     new () { Nome = "Soraia", Nascimento = new DateTime(2004, 1, 16) },
     new () { Nome = "Salvador", Nascimento = new DateTime(1975, 10, 11) },
     new () { Nome = "Benedita", Nascimento = new DateTime(1966, 9, 30) },
     new () { Nome = "Silvia", Nascimento = new DateTime(1984, 3, 17) },
};

O nosso objetivo será obter o resultado da união da primeira lista de usuários com a segunda lista com base no ano de nascimento. Para isso vamos definir a consulta :

var uniaoPorAnoNascimento = usuarios1.UnionBy(usuarios2, x => x.Nascimento.Year);

Este cálculo "favorece" a primeira lista ou conjunto, porque se os usuários da segunda lista tiverem o mesmo ano de nascimento que os usuários da primeira lista, os usuários da primeira lista serão incluídos e os da segunda não.

Aqui também podemos ordenar o resultado usando : OrderBy(x => x.Nascimento.Year);

O código completo é dado abaixo:

var usuarios1 = new[]
{
    new Usuario() { Nome = "Marta", Nascimento = new DateTime(1985, 12, 6) },
    new Usuario() { Nome = "Ricardo", Nascimento = new DateTime(1984, 8, 22) },
    new Usuario() { Nome = "Amanda", Nascimento = new DateTime(2001, 7, 29) },
    new Usuario() { Nome = "Jaime", Nascimento = new DateTime(2004, 1, 16) },
};

var usuarios2 = new Usuario[]
{
     new () { Nome = "Felício", Nascimento = new DateTime(2001, 6, 1)},
     new () { Nome = "Soraia", Nascimento = new DateTime(2004, 1, 16) },
     new () { Nome = "Salvador", Nascimento = new DateTime(1975, 10, 11) },
     new () { Nome = "Benedita", Nascimento = new DateTime(1966, 9, 30) },
     new () { Nome = "Silvia", Nascimento = new DateTime(1984, 3, 17) },
};

Console.WriteLine("\n### Usando UnionBy ###\n");

var uniaoPorAnoNascimento = usuarios1.UnionBy(usuarios2, x => x.Nascimento.Year);
                          

foreach (var usuario in uniaoPorAnoNascimento)
    Console.WriteLine($"\t{usuario.Nome} - {usuario.Nascimento.Year}");

Console.ReadKey();

public record Usuario
{
    public string? Nome { get; set; }
    public DateTime Nascimento { get; set; }
}

Obs: Aqui usamos o recurso das instruções de nível superior

Executando o projeto teremos o seguinte resultado:

Com o uso destes métodos temos um código mais sucinto e legível e que realiza a tarefa proposta em apenas uma etapa.

Pegue o código dos dois projetos aqui :   linq2.zip

"Multidões que dormem no pó da terra acordarão: uns para a vida eterna, outros para a vergonha, para o desprezo eterno.
Aqueles que são sábios reluzirão como o brilho do céu, e aqueles que conduzem muitos à justiça serão como as estrelas, para todo o sempre."
Daniel 12:2,3

Referências:


José Carlos Macoratti