2

扩展我的 linq 查询时遇到一些困难。

假设我有以下数据:

Vault      Items
-----      -----
1          100, 102
2          100, 102
3          101
4          101

我想展平这个数组,所以我得到了这个:

Items      Vaults
-----      ------
100, 102   1, 2
101        3, 4

我目前拥有的代码是这个

using System;
using System.Linq;
using System.Collections.Generic;


class Program
{
    static void Main()
    {
        var Vaults = new[]
            {
                new Vault
                    {
                        Id = 1,
                        Contents = new Contents
                            {
                                Items = new[]
                                    {
                                        new Item
                                            {
                                                Id = 100,
                                                Number = "100"
                                            },
                                        new Item
                                            {
                                                Id = 102,
                                                Number = "102"
                                            }
                                    }
                            }
                    },
                new Vault
                    {
                        Id = 2,
                        Contents = new Contents
                            {
                                Items = new[]
                                    {
                                        new Item
                                            {
                                                Id = 100,
                                                Number = "100"
                                            },
                                        new Item
                                            {
                                                Id = 102,
                                                Number = "102"
                                            }
                                    }

                            }
                    },
                new Vault
                    {
                        Id = 3,
                        Contents = new Contents
                            {
                                Items = new[]
                                    {
                                        new Item
                                            {
                                                Id = 101,
                                                Number = "101"
                                            }
                                    }
                            }
                    },
                new Vault
                    {
                        Id = 4,
                        Contents = new Contents
                            {
                                Items = new[]
                                    {
                                        new Item
                                            {
                                                Id = 101,
                                                Number = "101"
                                            }
                                    }
                            }
                    }
            };


        var grouping = Vaults.SelectMany(
            c => c.Contents.Items.Select(s => new { Item = s, Vault = c }))
                                .GroupBy(o => o.Item.Id)
                                .Select(
                                    grouping1 =>
                                    new Tuple<Item, IEnumerable<Vault>>(
                                        grouping1.First().Item, grouping1.Select(o => o.Vault)));

        foreach (var tuple in grouping)
        {
            Console.WriteLine("Item(s) {0}: Vault {1}", tuple.Item1.Number,
                              String.Join(", ", tuple.Item2.Select(c => c.Id.ToString()).ToArray()));
        }
    }
}



public class Vault
{
    public long Id { get; set; }
    public Contents Contents { get; set; }
}

public class Contents
{
    public IEnumerable<Item> Items { get; set; }
}

public class Item
{
    public long Id { get; set; }
    public string Number { get; set; }
}

这个查询还不能很好地工作,因为它没有在项目列表上分组,结果类似于:

Item       Vaults         //Note: this isn't the output I need
----       ------
100        1, 2
102        1, 2
101        3, 4

谁能指出我修复此查询的正确方向?对于这个例子,我们可以假设 Vault 共享的项目总是相同的。(意味着 Vault 2必须包含与 Vault 1 完全相同的物品)

4

1 回答 1

2

你可以这样做:

首先,添加GetHashCodeEqualsItem如下所示:

public class Item {
    public long Id { get; set; }
    public string Number { get; set; }
    public override bool Equals(object obj) {
        if (obj == this) return true;
        var other = obj as Item;
        if (other == null) return false;
        return Id == other.Id;
    }
    public override int GetHashCode() {
        return Id.GetHashCode();
    }
}

接下来,定义一个可以比较项目集是否相等的类,如下所示:

internal class MultipartKey<T> {
    private readonly HashSet<T> items;
    private readonly int hashCode;
    public MultipartKey(IEnumerable<T> items) {
        this.items = new HashSet<T>(items);
        hashCode = this.items.Where(i => i != null)
           .Aggregate(0, (p, v) => p*31 + v.GetHashCode());
    }
    public override int GetHashCode() {
        return hashCode;
    }
    public override bool Equals(object obj) {
        if (obj == this) return true;
        var other = obj as MultipartKey<T>;
        if (other == null) return false;
        return items.SetEquals(other.items);
    }
}

现在您的查询可以表述如下:

var grouping = Vaults
    .GroupBy(v => new MultipartKey<Item>(v.Contents.Items))
    .Select(g => new {
        Items = g.First().Contents.Items
    ,   Vaults = g.ToList()
    });
    foreach (var g in grouping) {
        Console.WriteLine("{0} ---- {1}"
        ,   string.Join(",", g.Items.Select(i=>i.Id))
        ,   string.Join(",", g.Vaults.Select(v => v.Id))
        );
    }
于 2013-09-23T13:43:27.627 回答