Select和SelectMany之间的区别


1071

我一直在寻找和之间的区别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

Answers:


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

谢谢您的拨弄链接!
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 2014年

4
SelectMany不必那样使用。它也可以选择只使用一个功能。
barlop '16

2
我不知道,如果它是正确的说,这是多么SelectMany 。而是,这是一种SelectMany可以使用的方法,但实际上不是正常的使用方法。
Dave Cousineau

1
这是我最容易理解的答案。
Chaim Eliyah's

如果您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. 阿尔巴
  3. 哥斯达黎加
  4. 别墅
  5. 布斯克茨

...


9
很好的示例数据
ben_mj

1
您能添加一个示例供选择以完成此答案吗:)
哈里(Harry)

73

SelectMany()使您可以以某种方式折叠多维序列,而这种折叠本来需要一秒钟Select()或循环。

有关更多详细信息,请参见此博客文章


但是第一个返回儿童的Enumerables类型,第二个返回父母的示例类型?其实我有点困惑,您能再打开一点吗?
塔里克

相反,实际上。第二个将完全弄平可枚举的层次结构,以便您可以使Children返回。在我添加的链接上尝试文章,看看是否有帮助。
Michael Petrotta

第一个似乎不合法。我认为海报使自己感到困惑。第二个将返回可数的父母。
mqp

谢谢,实际上,是的,这些示例有点令人困惑:),但再次感谢您尝试帮助我。
塔里克2009年

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
马特

13

Select是从源元素到结果元素的简单一对一投影。当查询表达式中有多个from子句时,将使用Select- Many:原始序列中的每个元素都将用于生成新序列。


7

某些SelectMany可能不是必需的。下面两个查询给出相同的结果。

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

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

对于一对多关系,

  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

更清楚的是查询何时返回字符串(char数组):

例如,如果列表“水果”包含“苹果”

“选择”返回字符串:

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

仅出于替代的观点,可能对某些功能性程序员有所帮助:

  • Selectmap
  • SelectManybind(或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();

因此,如您所见,由于“ SelectMany”变平并在多个序列中投影,因此已从query2中删除了诸如“ I”或“ like”之类的重复值。但是query1返回字符串数组的序列。并且由于query1中有两个不同的数组(第一个和第二个元素),因此不会删除任何内容。


现在最好在末尾包含.Distinct()并声明它输出“ I”,“喜欢”,“什么”,“ 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.