Select和SelectMany之间的区别

时间:2009-06-06 03:54:17

标签: c# linq-to-sql linq

我一直在寻找SelectSelectMany之间的区别,但我找不到合适的答案。我需要了解使用LINQ To SQL时的不同之处,但我发现的只是标准数组示例。

有人可以提供LINQ To SQL示例吗?

18 个答案:

答案 0 :(得分:1444)

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

Live Demo on .NET Fiddle

答案 1 :(得分:163)

选择多个就像cross join operation in 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)}

答案 2 :(得分:103)

enter image description here

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. 科斯塔
  3. 别墅
  4. 布斯克茨
  5. ...

答案 3 :(得分:73)

SelectMany()允许您以一种需要第二个Select()或循环的方式折叠多维序列。

blog post的详细信息。

答案 4 :(得分:33)

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's IK blog。我强烈建议你看看它。

答案 5 :(得分:19)

我理解SelectMany可以像连接快捷方式一样工作。

所以你可以:

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

答案 6 :(得分:12)

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

答案 7 :(得分:10)

SelectMany()方法用于展平序列,其中序列中的每个元素都是单独的。

我的课程user像这样

class User
    {
        public string UserName { get; set; }
        public List<string> Roles { get; set; }
    }

主要

var users = new List<User>
            {
                new User { UserName = "Reza" , Roles = new List<string>{"Superadmin" } },
                new User { UserName = "Amin" , Roles = new List<string>{"Guest","Reseption" } },
                new User { UserName = "Nima" , Roles = new List<string>{"Nurse","Guest" } },
            };

var query = users.SelectMany(user => user.Roles, (user, role) => new { user.UserName, role });

foreach (var obj in query)
{
    Console.WriteLine(obj);
}
//output

//{ UserName = Reza, role = Superadmin }
//{ UserName = Amin, role = Guest }
//{ UserName = Amin, role = Reseption }
//{ UserName = Nima, role = Nurse }
//{ UserName = Nima, role = Guest }

您可以对任何序列项进行操作

int[][] numbers = {
                new[] {1, 2, 3},
                new[] {4},
                new[] {5, 6 , 6 , 2 , 7, 8},
                new[] {12, 14}
            };

IEnumerable<int> result = numbers
                .SelectMany(array => array.Distinct())
                .OrderBy(x => x);

//output

//{ 1, 2 , 2 , 3, 4, 5, 6, 7, 8, 12, 14 }
 List<List<int>> numbers = new List<List<int>> {
                new List<int> {1, 2, 3},
                new List<int> {12},
                new List<int> {5, 6, 5, 7},
                new List<int> {10, 10, 10, 12}
            };

 IEnumerable<int> result = numbers
                .SelectMany(list => list)
                .Distinct()
                .OrderBy(x=>x);

//output

// { 1, 2, 3, 5, 6, 7, 10, 12 }

答案 8 :(得分: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”过滤,这也比标准联接查询更简单)
  3. from o in Orders
    join c in Customers on o.CustomerID equals c.ID
    where c.Name == "Tom"
    select o
    

答案 9 :(得分: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列表。

第一个&#34;项目&#34;从组织到用户,第二个直接查询Users表。

答案 10 :(得分:2)

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

例如,如果列表'Fruits'包含'apple'

'选择'返回字符串:

 10 s
  9 t
  8 i
  4 r
  4 h
  4 e
  3 p
  3 l
  3 c
  2 o
  2 m
  2 a
  1 u
  1 n
  1 f

'SelectMany'展平字符串:

Fruits.Select(s=>s) 

[0]: "apple"

答案 11 :(得分:2)

只是为了一个可以帮助一些功能性程序员的替代视图:

  • Selectmap
  • SelectManybind(或flatMap为您的Scala / Kotlin人员)

答案 12 :(得分: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();

答案 13 :(得分:0)

下面是一个代码示例,其中包含用于测试的初始化小集合:

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

答案 14 :(得分:0)

考虑以下示例:

from pet import Pet as p
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: arg(): could not convert default argument into a Python object (type not registered yet?). Compile in debug mode for more information.

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

答案 15 :(得分:0)

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

答案 16 :(得分:-1)

另一种看待它的方式。

var src = new[] { 1, 2, 3, };
var map = src.Select(i => i.ToString());
var flatMap = src.SelectMany(i => /* go wild here producing IEnumerable's */);

var empty = src.SelectMany(_ => new string[0]);

map 的长度将总是匹配 src 的长度。 map 的元素将是任何转换 - 上面示例中的 string,如果它是某个 IEnumerable,它仍将被“包装”。

另一方面,根据为每个元素提供的转换,flatMap 的长度将是您想要的任何长度,因为所有这些可迭代项都将被“压缩”为一个。因此在极端情况下,empty 的长度将为 0,尽管 src.Length = 3

所以 SelectMany 可以被认为是丢弃原来的 IEnumerable

答案 17 :(得分:-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();

乘法表示例。