GroupBy 两个变量,其中一个来自嵌套列表

Posted

技术标签:

【中文标题】GroupBy 两个变量,其中一个来自嵌套列表【英文标题】:GroupBy two variables, one of them is from a nested list 【发布时间】:2021-08-24 19:26:06 【问题描述】:

我在按两个变量(在本例中为:Item.Name 和 Category.CategoryId)对项目进行分组时遇到问题,其中一个来自嵌套列表。

使用我的代码进行分组似乎效果不佳。 另外,如果可能的话,我希望得到一个字典。

我正在努力实现这样的目标:

Name "1", CategoryId "2" 
Name "1", CategoryId "2"

-----------------------

Name "1", CategoryId "3"

-----------------------

Name "2", CategoryId "2" 
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp

    public class Program
    
        static Dictionary<GroupingStruct, List<Item>> result;
        static void Main(string[] args)
        
            List<Item> items = new List<Item>()
                
                    new Item  Name = "Name 1",
                    new Item  Name = "Name 1",
                    new Item  Name = "Name 1",
                    new Item  Name = "Name 2",
                ;

            items[0].Categories.Add(new Category  CategoryId = "Category Id 2", Value = 1 );
            items[0].Categories.Add(new Category  CategoryId = "Category Id 3", Value = 5 );
            items[1].Categories.Add(new Category  CategoryId = "Category Id 2", Value = 7 );
            items[2].Categories.Add(new Category  CategoryId = "Category Id 2", Value = 9 );

            result = items.SelectMany(i => i.Categories.Select(x => new  It = i, Cat = x ))
                .GroupBy(g => new GroupingStruct()
                
                    ItemName = g.It.Name,
                    CategoryId = g.Cat.CategoryId,
                )
                .ToDictionary(k => k.Key, v => v.Select(x => x.It).ToList());
        
    

    public class Item
    
        public string Name  get; set; 
        public List<Category> Categories  get; set;  = new List<Category>();
        public Guid Guid  get; set;  = Guid.NewGuid();
    

    public class Category
    
        public string CategoryId  get; set; 
        public int Value  get; set; 
    

    public struct GroupingStruct
    
        public string ItemName  get; set; 
        public string CategoryId  get; set; 
    

感谢您的帮助!

【问题讨论】:

有什么理由需要GroupingStruct?匿名类型为您正确实现相等(例如GroupBy(i =&gt; new i.It.Name, i.Cat.CategoryId ))。如果您需要跨模块访问,请考虑使用ValueTupleGroupBy(i =&gt; (i.It.Name, i.Cat.CategoryId)) GroupingStruct 只是我尝试的最后一种形式。我已经覆盖了 GetHashCode 并添加了 Equal Method。但我认为这不是我的主要问题。 【参考方案1】:

.GroupBy() 需要 GroupingStructIEquatable&lt;GroupingStruct&gt; 接口实现 Equals 方法,因为它是 LINQ 的一部分。您还应该覆盖 object.GetHashCode 和 'object.Equals' 以保持一致性。 LINQCollections 通常使用这些方法来实现相等性。

public struct GroupingStruct: IEquatable<GroupingStruct> 
  public string ItemName 
    get;
    set;
  

  public string CategoryId 
    get;
    set;
  

  public bool Equals(GroupingStruct other) 
    return ItemName == other.ItemName && CategoryId == other.CategoryId;
  

  public override bool Equals(object obj)
  
     if (!(obj is GroupingStruct))
     return false;

     return Equals((GroupingStruct) obj);
  

  public override int GetHashCode() 
    return string.Format("01", ItemName, CategoryId).GetHashCode();
  

【讨论】:

是的,也许这是一个好的方向,但是在实施您的更改后,结果是相同的,分组效果不佳。我也使用了 struct 因为类我的代码没有意义,所以不知道 GetHashCode 在这里是否也有 sens ? @YorbGG GroupBy 使用这两种方法。您可以通过设置断点或在控制台上打印一些东西来检查。 @YorbGG 我的错,我想我应该为 Dictionary 覆盖object.Equals 我现在明白了。对我来说仍然很奇怪,Equals 两次返回“真”,但我仍然在一组中有 3 个项目,在第二组中有 1 个。应该是3组,因为我们有两个匹配,没有两个不同。另外我认为在这里使用 object.Equals 是错误的,我们需要复制字符串/结构,没有引用? @YorbGG GroupBy 主要使用 GetHashCode。如果哈希值相同,则调用IEquatable.Equals【参考方案2】:

使用匿名类型作为键将允许GroupBy 匹配所有成员,然后您可以选择项目。

var result = items
    .SelectMany(i => i.Categories.Select(c => new  Key = new  i.Name, c.CategoryId , Item = i ))
    .GroupBy(ki => ki.Key)
    .ToDictionary(ki => ki.Key, ki => ki.Select(x => x.Item).ToList());

如果您需要在创建result的方法之外使用Dictionary,可以将匿名类型替换为ValueTuple

var result = items
    .SelectMany(i => i.Categories.Select(c => new  Key = (i.Name, c.CategoryId), Item = i ))
    .GroupBy(ki => ki.Key)
    .ToDictionary(ki => ki.Key, ki => ki.Select(x => x.Item).ToList());

【讨论】:

感谢您的帮助,但它不能按我的意愿工作。例如,对于键 'Name 1' 和 CategoryId '2' 它返回带有 'Name 1' CategoryId '2' 的项目(值)(没关系),但也返回 Category '3' - 所以这是错误的。它应该只返回 'Name 1' CategoryId '2' 和 'Name 1' 和 Category '3' 作为新的对。 @YorbGG “退货”是什么意思? Name 1Category 2Category 3 中,因此它包含在每个键中。名为Name 1 的对象在其Categories 列表中同时具有Category 2Category 3,因此Category 3 显示在键Category 2 下。由于Name 1 有两个类别,它会出现两次,它是同一个对象。你没有提到创建新对象。 我所说的“返回”是指结果字典中最后的内容。 @YorbGG 您的示例代码没有向“名称 2”添加任何类别,因此它不包含在输出中。你能修复你的代码或预期的输出来匹配吗?

以上是关于GroupBy 两个变量,其中一个来自嵌套列表的主要内容,如果未能解决你的问题,请参考以下文章

如何执行嵌套 Thymeleaf 循环以创建包含来自两个 JPA 表类的字段的表

Scala groupBy 项目列表中的所有元素

来自 groupby 多列的 bin 大小的嵌套字典

使用 GroupBy 从 List 中创建子列表,其中 GroupBy 值为 List<t>

来自嵌套单词列表的共现矩阵

Seaborn通过多个groupby绘制熊猫数据框