Select와 SelectMany의 차이점


1072

나는 사이의 차이를 검색했습니다 Select및 SelectMany하지만 적절한 답을 찾을 수 없어. 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.