关于c#:select和selectmany之间的差异

Difference Between Select and SelectMany

我一直在寻找SelectSelectMany之间的区别,但我没有找到合适的答案。我需要了解使用linq-to-sql时的区别,但我发现的只是标准数组示例。

有人能提供linq-to-sql示例吗?


SelectMany使返回列表的查询变平。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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上的实时演示


select many类似于SQL中的交叉联接操作,它采用交叉积。例如,如果我们

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

select many可用于获取以下集合

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

请注意,这里我们采用集合A和集合B的元素可以进行的所有可能的组合。

下面是一个您可以尝试的LINQ示例

1
2
3
4
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 });

混合料的平面结构如下:

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


enter image description here

1
2
3
4
5
6
7
var players = db.SoccerTeams.Where( c=> c.Country =="Spain")
                            .SelectMany( c => c.players);

foreach(var player in players)
{
    Console.WriteLine( player.LastName);
}
  • 德加
  • 阿尔巴
  • 科斯塔
  • 别墅
  • 布斯克茨

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

    有关详细信息,请访问此日志。


    SelectMany有多个过载。其中之一允许您在遍历层次结构时跟踪父级和子级之间的任何关系。

    示例:假设您具有以下结构:League -> Teams -> Player

    你可以很容易地退回一组玩家。但是,你可能会失去对球员所在球队的任何参考。

    幸运的是,这样做有一个过载:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
                                       };

    前一个例子来自丹的博客。我强烈建议你看一下。


    我理解selectmany像一个连接快捷方式一样工作。

    所以你可以:

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


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


    可能不需要某些selectmany。下面的两个查询给出了相同的结果。

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

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

    对于一对多的关系,

  • 如果从"1"开始,则需要selectmany,它会使many变平。
  • 如果从"many"开始,则不需要selectmany。(仍然能够从"1"中筛选,而且这比标准联接查询更简单)
  • 1
    2
    3
    4
    from o in Orders
    join c in Customers on o.CustomerID equals c.ID
    where c.Name =="Tom"
    select o

    在不获取太多技术数据库的情况下,多个组织、多个用户:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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列表。

    第一个"项目"从组织到用户,第二个直接查询用户表。


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

    • Selectmap
    • SelectManybind(或flatMap是你的scala/kotlin人)

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

    例如,如果"水果"列表中包含"苹果"

    "select"返回字符串:

    1
    2
    3
    Fruits.Select(s=>s)

    [0]:"apple"

    "selectmany"使字符串变平:

    1
    2
    3
    4
    5
    6
    7
    Fruits.SelectMany(s=>s)

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


    再举一个例子,如何使用selectmany+select来累积子数组对象数据。

    假设我们有带电话的用户:

    1
    2
    3
    4
    5
    6
    7
    8
    class Phone {
        public string BasePart ="555-xxx-xxx";
    }

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

    现在,我们需要选择所有用户的所有手机基础部件:

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

    我认为这是理解的最好方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
                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();

    乘法表示例。