ความแตกต่างระหว่าง Select และ SelectMany


1072

ฉันได้รับการค้นหาความแตกต่างระหว่างSelectและSelectManyแต่ฉันไม่ได้รับสามารถที่จะหาคำตอบที่เหมาะสม ฉันต้องเรียนรู้ความแตกต่างเมื่อใช้ LINQ กับ SQL แต่ทั้งหมดที่ฉันพบคือตัวอย่างอาร์เรย์มาตรฐาน

ใครสามารถให้ตัวอย่าง LINQ กับ SQL ได้ไหม


8
คุณสามารถดูรหัสสำหรับ SelectMany กับฟังก์ชั่นหนึ่งหรือสองหน้าที่referencesource.microsoft.com/#System.Core/System/Linq/...
barlop

1
หากคุณคุ้นเคยกับ Kotlin มันมีการใช้งานที่คล้ายกันมากสำหรับคอลเลกชันเช่น map aka C # Select และ flatMap aka C # SelectMany โดยทั่วไปแล้วฟังก์ชันส่วนขยายไลบรารี stot Kotlin สำหรับคอลเลกชันมีความคล้ายคลึงกับไลบรารี 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 });

การสาธิตสดบน


1
คำถามที่เกี่ยวข้องกับการซ้อน SelectMany เพื่อปรับโครงสร้างโครงสร้างแบบซ้อนกันให้เรียบ
ถั่วแดง

1
เพื่อให้เข้าใจมากขึ้น resultSelector ลิงค์ด้านล่างจะช่วยให้blogs.interknowlogy.com/2008/10/10/...
Jamir

อีกหนึ่งการสาธิตด้วยผลลัพธ์จากผู้ปกครอง: dotnetfiddle.net/flcdCC
Evgeniy Kosjakov

ขอบคุณสำหรับลิงค์ซอ!
Aerin

197

มีหลายคนที่เลือกเช่นการดำเนินการ cross cross ใน SQLที่ใช้ผลิตภัณฑ์ cross
เช่นถ้าเรามี

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
นี่เป็นคำตอบที่ง่ายที่สุดสำหรับฉันที่จะเข้าใจ
ไคม์เอลียาห์

มันจะดีถ้าคุณแสดงให้เห็นถึงWhereสภาพหลังจาก SelectMany
Nitin 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. Alba
  3. คอสตา
  4. วิลล่า
  5. Busquets

...


9
ข้อมูลตัวอย่างที่ดี
ben_mj

1
คุณสามารถเพิ่มตัวอย่างสำหรับเลือกที่จะเสร็จสมบูรณ์คำตอบนี้ :)
แฮร์รี่

73

SelectMany()ให้คุณยุบลำดับหลายมิติในลักษณะที่อาจต้องใช้วินาทีSelect()หรือวนซ้ำ

รายละเอียดเพิ่มเติมได้ที่โพสต์บล็อกนี้


แต่สิ่งแรกที่ส่งคืนชนิดนับจำนวนเด็กตัวอย่างที่สองประเภทที่กลับมาของผู้ปกครอง? อันที่จริงฉันสับสนเล็กน้อยคุณจะเปิดมันเพิ่มอีกหน่อย
Tarik

วิธีอื่น ๆ จริง ๆ แล้ว ประการที่สองจะทำให้ลำดับชั้นของการแจกแจงแบนเรียบจนคุณสามารถนำ Children กลับมาได้ ลองบทความที่ลิงค์ที่ฉันเพิ่มดูว่าช่วยได้ไหม
Michael Petrotta

คนแรกดูเหมือนจะไม่ถูกกฎหมาย ฉันคิดว่าโปสเตอร์ตัวเองสับสน คนที่สองจะกลับมาเป็นจำนวนมากของผู้ปกครอง
mqp

ขอบคุณจริง ๆ ใช่แล้วตัวอย่างต่าง ๆ ค่อนข้างสับสน :) แต่ขอบคุณอีกครั้งที่พยายามช่วยฉัน
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 
                                   };

ตัวอย่างก่อนหน้านี้จะนำมาจากบล็อก 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 ค่อนข้างเรียบรายการรายการ - และคุณสามารถเลือกใด ๆ (แต่เพียงหนึ่งรายการในเวลาเดียว) ของรายการที่มีอยู่สำหรับผลลัพธ์ สำหรับการเปรียบเทียบ: Inner เข้าร่วมในการ Linq
Matt

13

เลือกเป็นการฉายภาพแบบหนึ่งต่อหนึ่งอย่างง่ายจากองค์ประกอบต้นฉบับไปยังองค์ประกอบผลลัพธ์ เลือก - จำนวนมากจะใช้เมื่อมีหลายคำสั่งในนิพจน์เคียวรี: แต่ละองค์ประกอบในลำดับเดิมจะถูกใช้เพื่อสร้างลำดับใหม่


7

SelectMany บางตัวอาจไม่จำเป็น ข้อความค้นหาด้านล่าง 2 ข้อความให้ผลลัพธ์เหมือนกัน

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

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

สำหรับความสัมพันธ์แบบ 1 ต่อหลายคน

  1. หากเริ่มต้นจาก "1" จำเป็นต้องเลือก SelectMany มันจะปรับให้แบนราบจำนวนมาก
  2. หากเริ่มจาก "หลายคน" ไม่จำเป็นต้องเลือก 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 เดียวกันสำหรับองค์กรที่เลือก

"โครงการ" แรกจากองค์กรถึงผู้ใช้คนที่สองสอบถามตารางผู้ใช้โดยตรง


3

มีความชัดเจนมากขึ้นเมื่อแบบสอบถามส่งคืนสตริง (อาร์เรย์ถ่าน):

ตัวอย่างเช่นหากรายการ 'ผลไม้' มี 'แอปเปิ้ล'

'เลือก' ส่งคืนสตริง:

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สำหรับคน Scala / Kotlin ของคุณ)

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

ดังนั้นเมื่อคุณเห็นค่าที่ซ้ำกันเช่น "I" หรือ "like" ถูกลบออกจาก query2 เนื่องจาก "SelectMany" แบนราบและโครงการข้ามหลายลำดับ แต่ query1 ส่งคืนลำดับของอาร์เรย์สตริง และเนื่องจากมีสองอาร์เรย์ที่แตกต่างกันใน query1 (องค์ประกอบที่หนึ่งและที่สอง) จะไม่มีการลบอะไรออก


อาจดีกว่าที่จะรวมตอนนี้ความแตกต่าง () ที่ส่วนท้ายและระบุไว้ว่า "ฉัน" "ชอบ" "อะไร" "ฉัน" "เหมือน" "ฉัน" "ชอบ" "อะไร" "คุณ" "ชอบ"
ศาสตราจารย์

1

อีกตัวอย่างหนึ่งที่สามารถใช้ SelectMany + Select เพื่อรวบรวมข้อมูลวัตถุอาร์เรย์ย่อย

สมมติว่าเรามีผู้ใช้ที่ใช้โทรศัพท์:

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

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

ตอนนี้เราต้องเลือก BaseParts ของโทรศัพท์ทั้งหมดของผู้ใช้ทั้งหมด:

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))
Michael Best

-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>เช่นคอมมิวนิสต์องค์ประกอบทุกคนจะประพฤติในลักษณะเดียวกัน (เป็นคนโง่มีสิทธิเช่นเดียวของ genious หนึ่ง)

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.