Diferença entre Select e SelectMany

1074

Estive pesquisando a diferença entre Selecte SelectManynão consegui encontrar uma resposta adequada. Preciso aprender a diferença ao usar o LINQ To SQL, mas tudo o que encontrei são exemplos de matriz padrão.

Alguém pode fornecer um exemplo de LINQ To SQL?

Tarik
fonte
8
você pode olhar para o código para SelectMany com uma função, ou com duas funções referencesource.microsoft.com/#System.Core/System/Linq/...
barlop
1
Se você conhece o Kotlin, ele tem implementações de coleções bastante semelhantes às do mapa, também conhecido como C # Select e flatMap, também conhecido como C # SelectMany. Basicamente, as funções de extensão de biblioteca Kotlin std para coleções têm semelhança com a biblioteca C # Linq.
Arsenius

Respostas:

1622

SelectManyfacilita as consultas que retornam listas de listas. Por exemplo

public class PhoneNumber
{
    public string Number { get; set; }
}

public class Person
{
    public IEnumerable<PhoneNumber> PhoneNumbers { get; set; }
    public string Name { get; set; }
}

IEnumerable<Person> people = new List<Person>();

// Select gets a list of lists of phone numbers
IEnumerable<IEnumerable<PhoneNumber>> phoneLists = people.Select(p => p.PhoneNumbers);

// SelectMany flattens it to just a list of phone numbers.
IEnumerable<PhoneNumber> phoneNumbers = people.SelectMany(p => p.PhoneNumbers);

// And to include data from the parent in the result: 
// pass an expression to the second parameter (resultSelector) in the overload:
var directory = people
   .SelectMany(p => p.PhoneNumbers,
               (parent, child) => new { parent.Name, child.Number });

Demonstração ao vivo no .NET Fiddle

Mike Two
fonte
1
Pergunta relacionada sobre o aninhamento do SelectMany para nivelar uma estrutura hierárquica aninhada.
The Red Pea
1
Para entender o resultadoSelecione mais O link abaixo ajuda blogs.interknowlogy.com/2008/10/10/…
jamir
Mais uma demonstração com os resultados dos pais: dotnetfiddle.net/flcdCC
Evgeniy Kosjakov
obrigado pelo link do violino!
Aerin
197

Selecionar muitos é como uma operação de junção cruzada no SQL, onde ele leva o produto cruzado.
Por exemplo, se tivermos

Set A={a,b,c}
Set B={x,y}

Selecione muitos podem ser usados ​​para obter o seguinte conjunto

{ (x,a) , (x,b) , (x,c) , (y,a) , (y,b) , (y,c) }

Observe que aqui tomamos todas as combinações possíveis que podem ser feitas a partir dos elementos do conjunto A e do conjunto B.

Aqui está um exemplo de LINQ que você pode tentar

List<string> animals = new List<string>() { "cat", "dog", "donkey" };
List<int> number = new List<int>() { 10, 20 };

var mix = number.SelectMany(num => animals, (n, a) => new { n, a });

a mistura terá os seguintes elementos em estrutura plana como

{(10,cat), (10,dog), (10,donkey), (20,cat), (20,dog), (20,donkey)}
Sriwantha Attanayake
fonte
4
Eu sei que isso é antigo, mas eu queria agradecer por isso, me salvou muito! :) Também pode ser útil ter uma referência a esses códigos: stackoverflow.com/questions/3479980/… Cheers!
user3439065
4
O SelectMany não precisa ser usado assim. Tem uma opção para apenas executar uma função também.
barlop
2
Não sei se é certo dizer que é assim que SelectMany é . Pelo contrário, esta é uma maneira que SelectManypode ser usada, mas na verdade não é a maneira normal de usá-lo.
Dave Cousineau 25/10
1
Esta foi a resposta mais simples para eu entender.
precisa saber é o seguinte
Seria bom se você também demonstrasse Wherecondição após SelectMany
Nitin Kt
126

insira a descrição da imagem aqui

var players = db.SoccerTeams.Where(c => c.Country == "Spain")
                            .SelectMany(c => c.players);

foreach(var player in players)
{
    Console.WriteLine(player.LastName);
}
  1. De Gea
  2. Alba
  3. Costa
  4. Moradia
  5. Busquets

...

AlejandroR
fonte
9
dados de exemplo excelente
ben_mj 7/03/19
2
você poderia adicionar um exemplo para selecionar para completar esta resposta :) #
Harry Harry
73

SelectMany()permite recolher uma sequência multidimensional de uma maneira que exigiria um segundo Select()ou loop.

Mais detalhes nesta postagem do blog .

Michael Petrotta
fonte
Mas o primeiro retorna o tipo Enumerables de Children o segundo exemplo retorna o tipo de Parent? Na verdade, estou um pouco confuso, você abriria um pouco mais?
Tarik
Ao contrário, na verdade. O segundo achatará completamente a hierarquia dos enumeráveis, para que você receba os filhos de volta. Experimente o artigo no link que eu adicionei, veja se isso ajuda.
22430 Michael Petrotta 06/06/2009
O primeiro não parece ser legal. Eu acho que o pôster ficou confuso. O segundo retornaria um número incontável de pais.
Mqp 6/06/09
Obrigado, bem, na verdade sim, os exemplos foram meio confusos, mas :) obrigado novamente por tentar me ajudar.
Tarik
37

Existem várias sobrecargas para SelectMany. Um deles permite rastrear qualquer relacionamento entre pai e filhos enquanto percorre a hierarquia.

Exemplo : suponha que você tem a seguinte estrutura: League -> Teams -> Player.

Você pode retornar facilmente uma coleção plana de jogadores. No entanto, você pode perder qualquer referência à equipe da qual o jogador faz parte.

Felizmente, há uma sobrecarga para esse fim:

var teamsAndTheirLeagues = 
         from helper in leagues.SelectMany
               ( l => l.Teams
                 , ( league, team ) => new { league, team } )
                      where helper.team.Players.Count > 2 
                           && helper.league.Teams.Count < 10
                           select new 
                                  { LeagueID = helper.league.ID
                                    , Team = helper.team 
                                   };

O exemplo anterior foi retirado do blog IK de Dan . Eu recomendo fortemente que você dê uma olhada nisso.

roland
fonte
19

Compreendo SelectMany funcionar como um atalho de associação.

Então você pode:

var orders = customers
             .Where(c => c.CustomerName == "Acme")
             .SelectMany(c => c.Orders);
Nathan Koop
fonte
O exemplo fornecido funciona, mas o SelectMany não funciona exatamente como uma junção. Uma junção permite "usar" qualquer campo da tabela original mais qualquer campo da tabela unida. Mas aqui você deve especificar um objeto de uma lista anexada à tabela original. Por exemplo, .SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});não funcionaria. SelectMany está bastante achatando a lista de listas - e você pode escolher qualquer (mas apenas uma de cada vez) das listas contidas para o resultado. Para comparação: junção interna no Linq .
19418 Matt
13

Selecionar é uma projeção individual simples do elemento de origem para o elemento de resultado. Selecionar - Muitos é usado quando há várias cláusulas from em uma expressão de consulta: cada elemento na sequência original é usado para gerar uma nova sequência.

Alexandr
fonte
7

Alguns SelectMany podem não ser necessários. Abaixo de 2 consultas, obtém o mesmo resultado.

Customers.Where(c=>c.Name=="Tom").SelectMany(c=>c.Orders)

Orders.Where(o=>o.Customer.Name=="Tom")

Para o relacionamento de um para muitos,

  1. se Iniciar a partir de "1", SelectMany for necessário, nivelará muitos.
  2. se Iniciar a partir de "Muitos", o SelectMany não é necessário. ( ainda é possível filtrar a partir de "1" , isso também é mais simples do que a consulta de junção abaixo do padrão)

from o in Orders
join c in Customers on o.CustomerID equals c.ID
where c.Name == "Tom"
select o
Rm558
fonte
4

Sem ser muito técnico - banco de dados com muitas organizações, cada um com muitos usuários: -

var orgId = "123456789";

var userList1 = db.Organizations
                   .Where(a => a.OrganizationId == orgId)
                   .SelectMany(a => a.Users)
                   .ToList();

var userList2 = db.Users
                   .Where(a => a.OrganizationId == orgId)
                   .ToList();

ambos retornam o mesmo lista ApplicationUser para a organização selecionada.

Os primeiros "projetos" da Organização para os Usuários, o segundo consulta diretamente a tabela Usuários.

RickL
fonte
3

É mais claro quando a consulta retorna uma string (uma matriz de caracteres):

Por exemplo, se a lista 'Frutas' contiver 'maçã'

'Select' retorna a string:

Fruits.Select(s=>s) 

[0]: "apple"

'SelectMany' nivela a string:

Fruits.SelectMany(s=>s)

[0]: 97  'a'
[1]: 112 'p'
[2]: 112 'p'
[3]: 108 'l'
[4]: 101 'e'
Eric Bole-Feysot
fonte
2

Apenas para uma visão alternativa que pode ajudar alguns programadores funcionais por aí:

  • Select é map
  • SelectManyé bind(ou flatMappara o seu pessoal da Scala / Kotlin)
Matt Klein
fonte
2

Considere este exemplo:

        var array = new string[2]
        {
            "I like what I like",
            "I like what you like"
        };
        //query1 returns two elements sth like this:
        //fisrt element would be array[5]  :[0] = "I" "like" "what" "I" "like"
        //second element would be array[5] :[1] = "I" "like" "what" "you" "like"
        IEnumerable<string[]> query1 = array.Select(s => s.Split(' ')).Distinct();

        //query2 return back flat result sth like this :
        // "I" "like" "what" "you"
        IEnumerable<string> query2 = array.SelectMany(s => s.Split(' ')).Distinct();

Portanto, como você vê, valores duplicados como "I" ou "like" foram removidos da query2 porque "SelectMany" nivela e projeta em várias seqüências. Mas query1 retorna a sequência de matrizes de string. e como existem duas matrizes diferentes na query1 (primeiro e segundo elemento), nada seria removido.

MG Lee
fonte
provavelmente é melhor incluir agora .Distinct () no final e indicar que gera "I" "como" "o que" "I" "como" "I" "como" "o que" "você" "gosta"
Prof
1

Mais um exemplo de como o SelectMany + Select pode ser usado para acumular dados de objetos de sub array.

Suponha que tenhamos usuários com seus telefones:

class Phone { 
    public string BasePart = "555-xxx-xxx"; 
}

class User { 
    public string Name = "Xxxxx";
    public List<Phone> Phones; 
}

Agora precisamos selecionar as BaseParts de todos os telefones de todos os usuários:

var usersArray = new List<User>(); // array of arrays
List<string> allBaseParts = usersArray.SelectMany(ua => ua.Phones).Select(p => p.BasePart).ToList();
KEMBL
fonte
Qual você acha que é melhor? Seu ouusersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
Michael Best
-1

Aqui está um exemplo de código com uma pequena coleção inicializada para teste:

class Program
{
    static void Main(string[] args)
    {
        List<Order> orders = new List<Order>
        {
            new Order
            {
                OrderID = "orderID1",
                OrderLines = new List<OrderLine>
                {
                    new OrderLine
                    {
                        ProductSKU = "SKU1",
                        Quantity = 1
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU2",
                        Quantity = 2
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU3",
                        Quantity = 3
                    }
                }
            },
            new Order
            {
                OrderID = "orderID2",
                OrderLines = new List<OrderLine>
                {
                    new OrderLine
                    {
                        ProductSKU = "SKU4",
                        Quantity = 4
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU5",
                        Quantity = 5
                    }
                }
            }
        };

        //required result is the list of all SKUs in orders
        List<string> allSKUs = new List<string>();

        //With Select case 2 foreach loops are required
        var flattenedOrdersLinesSelectCase = orders.Select(o => o.OrderLines);
        foreach (var flattenedOrderLine in flattenedOrdersLinesSelectCase)
        {
            foreach (OrderLine orderLine in flattenedOrderLine)
            {
                allSKUs.Add(orderLine.ProductSKU);
            }
        }

        //With SelectMany case only one foreach loop is required
        allSKUs = new List<string>();
        var flattenedOrdersLinesSelectManyCase = orders.SelectMany(o => o.OrderLines);
        foreach (var flattenedOrderLine in flattenedOrdersLinesSelectManyCase)
        {
            allSKUs.Add(flattenedOrderLine.ProductSKU);
        }

       //If the required result is flattened list which has OrderID, ProductSKU and Quantity,
       //SelectMany with selector is very helpful to get the required result
       //and allows avoiding own For loops what according to my experience do code faster when
       // hundreds of thousands of data rows must be operated
        List<OrderLineForReport> ordersLinesForReport = (List<OrderLineForReport>)orders.SelectMany(o => o.OrderLines,
            (o, ol) => new OrderLineForReport
            {
                OrderID = o.OrderID,
                ProductSKU = ol.ProductSKU,
                Quantity = ol.Quantity
            }).ToList();
    }
}
class Order
{
    public string OrderID { get; set; }
    public List<OrderLine> OrderLines { get; set; }
}
class OrderLine
{
    public string ProductSKU { get; set; }
    public int Quantity { get; set; }
}
class OrderLineForReport
{
    public string OrderID { get; set; }
    public string ProductSKU { get; set; }
    public int Quantity { get; set; }
}
Sharunas Bielskis
fonte
-2

Os SelectManymétodo derruba um IEnumerable<IEnumerable<T>>em um IEnumerable<T>, como o comunismo, cada elemento é comportado da mesma maneira (um cara estúpido tem mesmos direitos de um gênio).

var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }
snr
fonte
-5

É a melhor maneira de entender, eu acho.

            var query =
            Enumerable
                .Range(1, 10)
                .SelectMany(ints => Enumerable.Range(1, 10), (a, b) => $"{a} * {b} = {a * b}")
                .ToArray();

        Console.WriteLine(string.Join(Environment.NewLine, query));

        Console.Read();

Exemplo da tabela de multiplicação.

user5966157
fonte
4
Somente se o significado de "melhor" mudou dramaticamente.
Vahid Amiri
2
então esta é a melhor maneira de você pensar? então qual é a maneira difícil de pensar?
Syed Ali