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 => new i.It.Name, i.Cat.CategoryId )
)。如果您需要跨模块访问,请考虑使用ValueTuple
:GroupBy(i => (i.It.Name, i.Cat.CategoryId))
。
GroupingStruct 只是我尝试的最后一种形式。我已经覆盖了 GetHashCode 并添加了 Equal Method。但我认为这不是我的主要问题。
【参考方案1】:
.GroupBy()
需要 GroupingStruct
从 IEquatable<GroupingStruct>
接口实现 Equals
方法,因为它是 LINQ 的一部分。您还应该覆盖 object.GetHashCode
和 'object.Equals' 以保持一致性。 LINQ 和 Collections 通常使用这些方法来实现相等性。
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 ? @YorbGGGroupBy
使用这两种方法。您可以通过设置断点或在控制台上打印一些东西来检查。
@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 1
在 Category 2
和 Category 3
中,因此它包含在每个键中。名为Name 1
的对象在其Categories
列表中同时具有Category 2
和Category 3
,因此Category 3
显示在键Category 2
下。由于Name 1
有两个类别,它会出现两次,它是同一个对象。你没有提到创建新对象。
我所说的“返回”是指结果字典中最后的内容。
@YorbGG 您的示例代码没有向“名称 2”添加任何类别,因此它不包含在输出中。你能修复你的代码或预期的输出来匹配吗?以上是关于GroupBy 两个变量,其中一个来自嵌套列表的主要内容,如果未能解决你的问题,请参考以下文章
如何执行嵌套 Thymeleaf 循环以创建包含来自两个 JPA 表类的字段的表