【问题标题】:Grouping Deeply Nested Objects Using LINQ使用 LINQ 对深度嵌套的对象进行分组
【发布时间】:2017-12-15 20:43:51
【问题描述】:

我有以下课程:

public class Item
{
    public int Id { get; set; }
    public string Sku { get; set; }
    public List<Price> Prices { get; set; }
}

public class Price
{
    public int Id { get; set; }
    public double Cost { get; set; }
    public PriceList List { get; set; }
}

public class PriceList
{
    public int Id { get; set; }
    public string FileName { get; set; }
    public DateTime ImportDateTime { get; set; }
}

本质上,它是一个项目列表 [Item],其中每个项目都有一个价格列表 [List&lt;Price&gt; Prices](一对多),每个价格都有一个价格列表 [PriceList List] (一对一)。

我需要的是所有价目表的清单。

似乎我需要的是一个“孙子”组,基本上,按PriceListId 分组(在Price 下,又在Item 下在列表中)。

因此,例如,如果我有五个价目表,它应该返回五行。

我通过以下方式实现了它:

List<PriceList> priceLists = new List<PriceList>();

foreach (Item item in items)
{
    foreach (Price price in item.Prices)
    {
        PriceList list = price.List;

        if (!priceLists.Any(x => x.Id == list.Id))
        {
            priceLists.Add(list);
        }
    }
}

如何使用 LINQ 来实现?


更新:

这是一个基本的样本集:

PriceList priceList1 = new PriceList { Id = 1, FileName = "Price List 1.csv" };
PriceList priceList2 = new PriceList { Id = 2, FileName = "Price List 2.csv" };

Price price1 = new Price { Id = 1, Cost = 2.65, List = priceList1 };
Price price2 = new Price { Id = 2, Cost = 14.23, List = priceList2 };
Price price3 = new Price { Id = 3, Cost = 29.01, List = priceList1 };
Price price4 = new Price { Id = 4, Cost = 1, List = priceList2 };
Price price5 = new Price { Id = 5, Cost = 56.12, List = priceList1 };

Item item1 = new Item { Id = 1, Sku = "item1", Prices = new List<Price> { price1, price2 } };
Item item2 = new Item { Id = 2, Sku = "item2", Prices = new List<Price> { price3, price4 } };
Item item3 = new Item { Id = 3, Sku = "item3", Prices = new List<Price> { price5 } };

List<Item> itemsList = new List<Item> { item1, item2, item3 };

让我们从底部向上开始:

  1. 我有一个Item (itemsList) 列表,其中包含三个Items。
  2. 每个Item 都有一个Price 列表。
  3. 每个Price 有一个PriceList

为了澄清这一切背后的原因:用户导入他们定期从供应商那里获得的价格电子表格(价目表),价格会波动,并且每个价目表对相同商品都有不同的价格。因此,有一个商品有很多价格的原因,每个价格都有用于上传它的价目表(PriceList 类中包含更多信息,为了简单起见,我省略了)。

我希望我很清楚。

【问题讨论】:

  • 所以Price -> PriceList 并不是真正的one-to-one,而是many-to-one,最后是Item -> PriceListmany-to-manyPrice。和PriceList具有相同Id的对象不是同一个实例吗?
  • 您当前的方法是否产生分组结果集?我只能看到每个 PriceList Id 的一条记录(当我保留一些重复的 PriceList Id 时)。

标签: c# list linq group-by


【解决方案1】:

这似乎是你想要的,使用一个扩展来做 Distinct by a lambda 表达式:

var ans = itemsList.SelectMany(item => item.Prices.Select(price => price.List)).DistinctBy(price => price.Id);

扩展名如下:

public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keyFun) {
    var seenKeys = new HashSet<TKey>();
    foreach (T e in src)
        if (seenKeys.Add(keyFun(e)))
            yield return e;
}

如果您更改方法以使用 HashSet&lt;Int&gt; 来跟踪看到的 id,您可能会比 LINQ 更高效。

【讨论】:

    【解决方案2】:

    看起来您可以将SelectMany(以获取所有Prices 列表)与Select(以获取所有关联的PriceList 属性)结合起来:

    List<PriceList> priceLists = items.SelectMany(i => i.Prices).Select(p => p.List).ToList();
    

    编辑

    上面将返回所有PriceLists,但由于许多Items 可以引用相同的PriceList,那么您需要一种过滤PriceList.Id 字段的方法。

    一种方法是覆盖PriceList 对象的EqualsGetHashCode 属性。如果您确实认为两个 PriceList 对象具有相同的 Id 是相等的,那么您可以执行以下操作:

    public class PriceList
    {
        public int Id { get; set; }
        public string FileName { get; set; }
        public DateTime ImportDateTime { get; set; }
    
        public override bool Equals(object obj)
        {
            var other = obj as PriceList;
            return Id == other?.Id;
        }
    
        public override int GetHashCode()
        {
            return Id;
        }
    }
    

    这允许您在价格上使用Linq 方法、Distinct(它将调用Equals 方法来区分结果中的PriceLists

    List<PriceList> priceLists = items
        .SelectMany(i => i.Prices)
        .Select(i => i.List)
        .Distinct()
        .ToList();
    

    【讨论】:

    • 这给了我每个价格的价格表,所以如果我有一千个价格,我会得到一千个价格表作为回报。
    • 你说,“所以,例如,如果我有五个价目表,它应该返回五行。”
    • 我有“价格”和“价目表”,一个项目可以有多个价目表,每个价目表每个项目只有一个价格。因此,如果我有五个“价格表”,它应该返回五行(而不是如果我有五个“价格”,它应该返回五行)。
    • 哦,我想我明白了。许多Prices 可以引用相同的PriceList。您能否修改PriceList 对象以覆盖EqualsGetHashCode
    • 是的,许多Prices 可以引用相同的PriceList。不,我没有尝试覆盖任何东西,我该怎么做?
    猜你喜欢
    • 1970-01-01
    • 2021-10-02
    • 2019-01-10
    • 1970-01-01
    • 2013-12-03
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 1970-01-01
    相关资源
    最近更新 更多