简体   繁体   English

Linq - 根据属性将列表分组成对

[英]Linq - group a list into pairs based on a property

I have a list of objects with a property that can be used to partition the objects into pairs. 我有一个具有属性的对象列表,可用于将对象分成对。 I know in advance that each object is part of a pair. 我事先知道每个物体都是一对的一部分。

Here is an example to illustrate the situation: 这是一个说明情况的例子:


I have a list of individual shoes that I would like to group into pairs. 我有一个单独的鞋子列表,我想成对分组。

Let's say my list is as follows: 假设我的清单如下:

Pair:
Id: 19, Brand: Nike, LeftOrRight: L
Id: 29, Brand: Nike, LeftOrRight: R

Pair:
Id: 11, Brand: Nike, LeftOrRight: L
Id: 60, Brand: Nike, LeftOrRight: R

Pair:
Id: 65, Brand: Asics, LeftOrRight: L
Id: 82, Brand: Asics, LeftOrRight: R

I would like to output these shoes as pairs, like so: 我想把这双鞋作为成对输出,如下:

\nPair: 对:\nId: 19, Brand: Nike, LeftOrRight: L Id:19,品牌:Nike,LeftOrRight:L\nId: 29, Brand: Nike, LeftOrRight: R Id:29,品牌:Nike,LeftOrRight:R\n\nPair: 对:\nId: 11, Brand: Nike, LeftOrRight: L Id:11,品牌:Nike,LeftOrRight:L\nId: 60, Brand: Nike, LeftOrRight: R Id:60,品牌:Nike,LeftOrRight:R\n\nPair: 对:\nId: 65, Brand: Asics, LeftOrRight: L Id:65,品牌:Asics,LeftOrRight:L\nId: 82, Brand: Asics, LeftOrRight: R Id:82,品牌:Asics,LeftOrRight:R\n

Note that an individual shoe can only exist as part of a single pair. 请注意,单个鞋只能作为单个鞋的一部分存在。

I have tried the following code to group the shoes, but it is clearly missing the pairs: 我已经尝试了以下代码来分组鞋子,但显然缺少对:

 var pairsByBrand = shoes.GroupBy(s => s.Brand); foreach (var group in pairsByBrand) { Console.WriteLine("Pair:"); foreach (var shoe in group) { Console.WriteLine(shoe); } Console.WriteLine(); } 

What statements can be used to group these items into pairs? 可以使用哪些语句将这些项分组成对?

Pure functional LINQ, using SelectMany and Zip , yielding an IEnumerable of Tuple s: 纯函数LINQ,使用SelectManyZip ,产生一个IEnumerable of Tuple s:

IEnumerable<Tuple<Shoe, Shoe>> pairs = shoes
    .GroupBy(shoe => shoe.Brand)
    .SelectMany(brand=>
        Enumerable.Zip(
            brand.Where(shoe=>shoe.LeftOrRight == LeftOrRight.L),
            brand.Where(shoe=>shoe.LeftOrRight == LeftOrRight.R),
            Tuple.Create
        )
    );
var shoesByBrand = shoes.GroupBy(s => s.Brand);
foreach (var byBrand in shoesByBrand)
{
    var lefts = byBrand.Where(s => s.LeftOrRight == LeftOrRight.L);
    var rights = byBrand.Where(s => s.LeftOrRight == LeftOrRight.R);
    var pairs = lefts.Zip(rights,(l, r) => new {Left = l, Right = r});

    foreach(var p in pairs)
    {
        Console.WriteLine("Pair:  {{{0}, {1}}}", p.Left.Id, p.Right.Id);
    }

    Console.WriteLine();
}

Note: Zip will only pair up as much as it can. 注意:Zip只会尽可能多地配对。 If you have extra rights or lefts they won't get reported. 如果您有额外的权利或左派,他们将无法报告。

One way to do it: 一种方法:

var pairs = shoes.GroupBy(s => s.Brand)
                 .Select(g => g.GroupBy(s => s.LeftOrRight));
                 .SelectMany(Enumerable.Zip(g => g.First(), g => g.Last(),Tuple.Create));

This is possibly an improvement on my initial idea (which has been nicely implemented by Thom Smith ) in that for each brand of shoes it splits them up in left and right shoes by iterating the collection only once. 这可能是我最初的想法( 由Thom Smith很好地实施 )的改进,因为对于每个品牌的鞋子,它只通过迭代一次只能将它们分成左右两双鞋子。 Gut feeling says it should be faster if there are brands with lots of shoes. 肠道感觉说如果有很多鞋子的品牌应该更快。

What it does is group the shoes by brand, then within each brand by left/right. 它的作用是按品牌分组鞋子,然后按左/右分组。 It then proceeds to randomly match left shoes of each brand with right shoes of the same, doing so for all brands in turn. 然后它会随机匹配每个品牌的左鞋和相同的合适的鞋子,依次对所有品牌这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM