Select와 SelectMany의 차이점


1072

나는 사이의 차이를 검색했습니다 SelectSelectMany하지만 적절한 답을 찾을 수 없어. LINQ To SQL을 사용할 때의 차이점을 알아야하지만 내가 찾은 것은 표준 배열 예입니다.

누군가 LINQ To SQL 예제를 제공 할 수 있습니까?


8
하나의 함수 또는 두 개의 함수를 사용하여 SelectMany의 코드를 볼 수 있습니다. referencesource.microsoft.com/#System.Core/System/Linq/…
barlop

1
Kotlin에 익숙하다면 map aka C # Select 및 flatMap aka C # SelectMany와 비슷한 컬렉션 구현이 있습니다. 컬렉션을위한 Kotlin std 라이브러리 확장 함수는 기본적으로 C # Linq 라이브러리와 유사합니다.
Arsenius

답변:


1618

SelectMany목록 목록을 반환하는 쿼리를 병합합니다. 예를 들어

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

.NET Fiddle의 라이브 데모


1
관련 질문 중첩 SelectMany에 중첩 된 계층 구조를 평평합니다.
붉은 완두콩

1
resultSelector을 이해하기 위해 더 링크 아래에 도움이 blogs.interknowlogy.com/2008/10/10/...
jamir

부모의 결과와 함께 한 가지 더 데모 : dotnetfiddle.net/flcdCC
Evgeniy Kosjakov

바이올린 링크 주셔서 감사합니다!
Aerin

197

많은 제품이 교차 제품을 취하는 SQL의 교차 조인 작업 과 유사 합니다.
예를 들어

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

많은 세트를 사용하여 다음 세트를 얻을 수 있습니다

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

여기서 우리는 세트 A와 세트 B의 요소로 만들 수있는 모든 가능한 조합을 취합니다.

시도 할 수있는 LINQ 예제는 다음과 같습니다.

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

믹스는 평평한 구조에서 다음과 같은 요소를 갖습니다.

{(10,cat), (10,dog), (10,donkey), (20,cat), (20,dog), (20,donkey)}

4
나는 이것이 오래되었다는 것을 알고 있지만, 이것에 대해 감사하고 싶었습니다. :)이 코드들에 대한 참조도 유용합니다 : stackoverflow.com/questions/3479980/… 건배!
user3439065

4
SelectMany를 그렇게 사용할 필요는 없습니다. 하나의 기능도 사용할 수있는 옵션이 있습니다.
barlop

2
그것은이 어떻게 말을 맞아 나도 몰라 SelectMany 입니다 . 오히려 이것은 SelectMany사용할 수 있는 방법 이지만 실제로는 일반적인 방법은 아닙니다.
Dave Cousineau

1
이것은 내가 이해하기 가장 간단한 대답이었습니다.
Chaim Eliyah

당신은 또한 보여 있다면 그것은 좋은 것입니다 Where후 SelectMany 조건을
니틴의 Kt

126

여기에 이미지 설명을 입력하십시오

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

foreach(var player in players)
{
    Console.WriteLine(player.LastName);
}
  1. 드 게아
  2. 알바
  3. 코스타
  4. 별장
  5. 부 스켓

...


9
훌륭한 예시 데이터
ben_mj

1
이 답변을 완성하기 위해 select에 대한 예를 추가 할 수 있습니까? :)
Harry

73

SelectMany()다른 방법으로 초 Select()또는 루프가 필요한 방식으로 다차원 시퀀스를 축소 할 수 있습니다 .

블로그 게시물에 대한 자세한 내용 .


그러나 첫 번째는 Enumerables 유형의 Children을 반환하고 두 번째 예제는 Parents를 반환합니까? 사실 좀 혼란스러워 조금 더 열어 줄래요?
Tarik

실제로 다른 방법. 두 번째는 열거 형의 계층 구조를 완전히 평평하게하여 자녀를 다시 얻습니다. 내가 추가 한 링크에서 기사를 사용해보십시오. 도움이되는지 확인하십시오.
Michael Petrotta 2016

첫 번째는 합법적이지 않은 것으로 보입니다. 포스터가 혼란 스러웠습니다. 두 번째는 열거 가능한 부모를 반환합니다.
mqp 2016

고마워, 실제로 예는 다소 혼란 스러웠습니다. :)하지만 나를 도와 주셔서 다시 한 번 감사드립니다.
Tarik

37

에 여러 가지 과부하가 SelectMany있습니다. 그 중 하나를 사용하면 계층을 순회하면서 부모와 자식 사이의 관계를 추적 할 수 있습니다.

: 다음 구조를 가지고 있다고 가정하십시오 League -> Teams -> Player.

플랫 플레이어 모음을 쉽게 반환 할 수 있습니다. 그러나 플레이어가 속한 팀에 대한 참조가 손실 될 수 있습니다.

다행히도 그러한 목적을위한 과부하가 있습니다.

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

이전 예는 Dan의 IK 블로그 에서 가져 왔습니다 . 나는 당신이 그것을 볼 것을 강력히 권장합니다.


19

SelectMany조인 바로 가기처럼 작동한다고 이해 합니다.

그래서 당신은 할 수 있습니다 :

var orders = customers
             .Where(c => c.CustomerName == "Acme")
             .SelectMany(c => c.Orders);

제공된 예제는 작동하지만 SelectMany 는 조인처럼 정확하게 작동하지 않습니다. 조인을 사용하면 원래 테이블의 필드와 조인 된 테이블의 필드를 "사용"할 수 있습니다. 그러나 여기서는 원래 테이블에 첨부 된 목록의 개체를 지정해야합니다. 예를 들어 .SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});작동하지 않습니다. SelectMany는 목록 목록을 편평하게 만들고 결과에 포함 된 목록 중 하나만 선택할 수 있습니다. 비교 : Linq의 내부 조인 .
Matt

13

선택은 소스 요소에서 결과 요소로의 간단한 일대일 투영입니다. Select- Many는 쿼리 식에 여러 from 절이있을 때 사용됩니다. 원래 시퀀스의 각 요소는 새 시퀀스를 생성하는 데 사용됩니다.


7

일부 SelectMany가 필요하지 않을 수 있습니다. 2 개 미만의 쿼리는 동일한 결과를 제공합니다.

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

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

일대 다 관계의 경우

  1. "1"에서 시작하는 경우 SelectMany가 필요하면 다수를 평탄화합니다.
  2. "Many"에서 시작하면 SelectMany가 필요하지 않습니다. ( 여전히 "1"에서 필터링 할 수 있으며 표준 조인 쿼리보다 간단합니다.

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

4

너무 많은 기술을 습득하지 않고 많은 조직이있는 데이터베이스, 각 사용자가 많은 경우 :-

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

둘 다 선택한 조직에 대해 동일한 ApplicationUser 목록을 리턴 합니다 .

첫 번째 "프로젝트"는 Organization에서 Users로, 두 번째는 Users 테이블을 직접 쿼리합니다.


3

쿼리가 문자열 (문자 배열)을 반환 할 때 더 분명합니다.

예를 들어 'Fruits'목록에 'apple'이 포함 된 경우

'Select'는 문자열을 반환합니다.

Fruits.Select(s=>s) 

[0]: "apple"

'SelectMany'는 문자열을 평평하게 만듭니다.

Fruits.SelectMany(s=>s)

[0]: 97  'a'
[1]: 112 'p'
[2]: 112 'p'
[3]: 108 'l'
[4]: 101 'e'

2

일부 기능 프로그래머에게 도움이 될 수있는 대체 견해를 위해 :

  • Select 이다 map
  • SelectMany이다 bind(또는 flatMap당신의 스칼라 / 코 틀린 사람들을위한)

2

이 예제를 고려하십시오.

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

"SelectMany"가 여러 시퀀스에 걸쳐 평평 해지고 투영되므로 "I"또는 "like"와 같은 중복 값이 ​​query2에서 제거되었습니다. 그러나 query1은 일련의 문자열 배열을 반환합니다. query1 (두 번째 요소 및 두 번째 요소)에 두 개의 다른 배열이 있으므로 제거 할 항목이 없습니다.


이제 끝 상태에서 .Distinct ()를 포함에 아마 더 나은은 "나는" ""무엇을 ""I ""와 같은 ""I ""와 같은 ""무엇을 ""당신 "" "같은처럼"출력
교수

1

하위 배열 객체 데이터를 축적하기 위해 SelectMany + Select를 사용하는 방법의 또 다른 예입니다.

전화가있는 사용자가 있다고 가정합니다.

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

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

이제 모든 사용자의 모든 전화기의 BasePart를 선택해야합니다.

var usersArray = new List<User>(); // array of arrays
List<string> allBaseParts = usersArray.SelectMany(ua => ua.Phones).Select(p => p.BasePart).ToList();

어느 쪽이 더 낫다고 생각합니까? 너 또는usersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
마이클 베스트

-1

다음은 테스트를 위해 초기화 된 작은 컬렉션이있는 코드 예제입니다.

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

SelectMany방법은 공산주의와 같은 IEnumerable<IEnumerable<T>>으로 넘어 가며 IEnumerable<T>, 모든 요소는 같은 방식으로 행동합니다 (멍청한 녀석은 똑같은 권리를가집니다).

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

-5

내가 생각하는 가장 좋은 방법입니다.

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

곱셈표 예.


4
"최고"의 의미가 극적으로 변경된 경우에만.
Vahid Amiri

2
그래서 이것은 당신이 생각하는 가장 좋은 방법입니다 ?? 그렇다면 어려운 생각의 방법은 무엇입니까 ??
Syed Ali
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.