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

时间:2012-09-07 15:01:29

标签: c# linq

我有一个对象列表,其中包含可用于将对象分成对的属性。我事先知道每个物体都是一对的一部分。

以下是一个说明情况的例子:

<小时/> 我有一个单独的鞋子列表,我想成对分组。

假设我的清单如下:

List<Shoe> shoes = new List<Shoe>();

shoes.Add(new Shoe { Id = 19, Brand = "Nike", LeftOrRight = LeftOrRight.L });
shoes.Add(new Shoe { Id = 29, Brand = "Nike", LeftOrRight = LeftOrRight.R });
shoes.Add(new Shoe { Id = 11, Brand = "Nike", LeftOrRight = LeftOrRight.L });
shoes.Add(new Shoe { Id = 60, Brand = "Nike", LeftOrRight = LeftOrRight.R });
shoes.Add(new Shoe { Id = 65, Brand = "Asics", LeftOrRight = LeftOrRight.L });
shoes.Add(new Shoe { Id = 82, Brand = "Asics", LeftOrRight = LeftOrRight.R });

我想成对输出这双鞋,如下:

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

请注意,单个鞋只能作为单个鞋的一部分存在。

我已尝试使用以下代码对鞋子进行分组,但显然缺少对:

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

可以使用哪些语句将这些项分组成对?

3 个答案:

答案 0 :(得分:8)

纯函数LINQ,使用SelectManyZip,产生IEnumerable 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
        )
    );

答案 1 :(得分:3)

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

注意:Zip只会尽可能多地配对。如果你有额外的权利或左派,他们就不会被报道。

答案 2 :(得分:2)

一种方法:

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

这可能是我最初的想法(很好implemented by Thom Smith)的一个改进,因为对于每个品牌的鞋子,它只通过迭代一次只能将它们分成左右两双鞋子。 Gut的感觉说,如果有很多品牌的鞋子应该会更快。

它的作用是按品牌分组鞋子,然后按左/右分组。然后,它会随机匹配每个品牌的左鞋和相同的鞋子,依次对所有品牌进行匹配。