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?


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


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

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

...


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 .


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.


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

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.


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

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.


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'

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)

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.


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();

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

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

-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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.