Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何对具有相同子对象列表的对象进行分组?_C#_List_Linq_Group By - Fatal编程技术网

C# 如何对具有相同子对象列表的对象进行分组?

C# 如何对具有相同子对象列表的对象进行分组?,c#,list,linq,group-by,C#,List,Linq,Group By,我正在尝试对对象列表进行分组,对象有一个属性,即列表。当使用以下表达式时,它不会将具有列表的对象分组在一起,可能是因为当它检查每个列表的相等性时,它不匹配-即使内容相同 我知道这就是问题所在,如果我停止对列表进行分组,代码将输出两项而不是三项 如何调整表达式,使其正确地将前两项(相同的)组合为一项 public class Item { public string Id { get; set; } public string Name { get; set; } public IEn

我正在尝试对对象列表进行分组,对象有一个属性,即列表。当使用以下表达式时,它不会将具有列表的对象分组在一起,可能是因为当它检查每个列表的相等性时,它不匹配-即使内容相同

我知道这就是问题所在,如果我停止对列表进行分组,代码将输出两项而不是三项

如何调整表达式,使其正确地将前两项(相同的)组合为一项

public class Item {
  public string Id { get; set; }
  public string Name { get; set; }
  public IEnumerable<ChildItem> Children { get; set; }
}

public class ChildItem {
  public string ChildItemName { get; set; }
}
公共类项目{
公共字符串Id{get;set;}
公共字符串名称{get;set;}
公共IEnumerable子项{get;set;}
}
公共类子项{
公共字符串ChildItemName{get;set;}
}
var items=新列表{
新项目{
Id=“00001”,
Name=“我的物品”,
儿童=新列表{
新的ChildItem{ChildItemName=“string 1”},
新的ChildItem{ChildItemName=“string 2”},
}
},
新项目{
Id=“00001”,
Name=“我的物品”,
儿童=新列表{
新的ChildItem{ChildItemName=“string 1”},
新的ChildItem{ChildItemName=“string 2”},
}
},
新项目{
Id=“00002”,
Name=“我的第二项”,
儿童=新列表{
新的ChildItem{ChildItemName=“string 3”},
}
}
};
var结果=
从项目中的c开始
c组由纽约
{
c、 身份证,
c、 名字,
c、 孩子们,
}
进入地面军事系统
选择新项目()
{
Id=gcs.Key.Id,
Name=gcs.Key.Name,
Children=gcs.Key.Children
};

您可以这样编写GroupBy表达式

items.GroupBy(x => x.Id);

如果具有相同Id的项目具有相同的名称和集合,则可以仅按Id分组,然后获取组中的第一个项目

IEnumerable<Item> uniqueItems = items
    .GroupBy(x => x.Id)
    .Select(g => g.First());
的实现:

class ItemEqualityComparer : IEqualityComparer<Item>
{
    public bool Equals(Item x, Item y)
    {
        return x.Id == y.Id && x.Name == y.Name &&
            Enumerable.SequenceEqual(x.Children, y.Children,
                ChildItemEqualityComparer.Instance);
    }

    public int GetHashCode(Item item)
    {
        int hash = 43;
        unchecked {
            hash = 17 * hash + (item.Id?.GetHashCode() ?? 0);
            hash = 17 * hash + (item.Name?.GetHashCode() ?? 0);
            foreach (ChildItem childItem in item.Children) {
                hash = 17 * hash + ChildItemEqualityComparer.Instance.GetHashCode(childItem);
            }
        }
        return hash;
    }
}

这将产生两个项目。

如果您只想消除重复项,您可以将集合转换为
哈希集,然后再转换回来

items.ToHashSet().ToList();
不过,您需要覆盖
GetHashCode
的默认实现,以确保它是使用
Item
的字段计算的

  • 如果必须使用ChildItem类(而不是简单的字符串),则 可以考虑为ChildItem实现IEQualyExpor。这样就可以使用 (System.Linq)SequenceEqual方法
  • 然后您可以考虑在Item类中实现IEquatable接口 IEquatable接口定义了Equals方法,该方法 确定实现类型的实例的相等性

    在Equals方法中,您可以使用SequenceEqual来检查上面描述的相等子级

    3.最后,您可以使用(System.Linq)Distinct()方法获取项的唯一对象

    IEnumerable<Item> noduplicates = items.Distinct();
    
    IEnumerable noduplicates=items.Distinct();
    
    请查收:


    是的,我可以编写稍有不同的语法,但关于列表相等性的问题仍然存在,我不想按ID分组,我想按对象中的所有属性分组。你说的是获取唯一元素还是分组?数组中有相同的项,我想消除重复项,但由于对象具有IEnumerable/列表,group by不会消除重复项。group by不应消除您拥有的任何数据。。把它们简单地分组。如果你想消除重复项,那么你必须做得有点不同。@TomMcClean a group by从不消除任何东西,它检查是否相等,而不是更多。另一方面,如果对象不相等,则不存在重复项。你似乎对being equal有不同的定义。覆盖
    Equals
    &
    GetHashCode
    用于
    ChildItem
    。不要听@JohnathanBarclay的建议,除非你100%确定自己在做什么,这两个覆盖意味着什么,并且100%确保这不会与任何其他内容冲突,也不会导致代码的其他部分出现问题。根据您的域逻辑,您可能希望在不同的地方以不同的方式“比较”相同的内容。那些
    覆盖
    将不允许您这样做。此外,还有许多代码(库)依赖于.Equals和.GetHashCode来完成各种事情,而和重写.Equals可能会导致微妙的错误。使用Olivier回答中的
    IEqualityComparer
    。这样更安全。我已经编辑了标题,让它更清楚一点。我认为“具有IEnumerable属性的组”并没有告诉您想要实现什么。此外,除非必要,否则我们通常不会在标题中包含关键字,因此我从标题()中删除了C#和LINQ标记。当然,您可以随意重新编辑标题,甚至可以根据需要回滚标题。为什么不按
    Id
    而不是
    子对象来比较对象呢?比较
    int
    s比
    List
    s更有效。ToHashSet将IEqualityComparer作为第二个参数。使用它而不是重写GHC更安全,除非您100%确定这种重写是安全的(例如,“item”类是您的私有实现细节,并且没有在其他地方的任何其他排序/字典/等中使用,等等)Good point@quetzalcatl。在本例中,
    Item
    似乎只是Tom的实现,但在其他情况下,使用
    IEqualityComparer
    更有意义。
    var result = items.Distinct(new ItemEqualityComparer());
    
    items.ToHashSet().ToList();
    
    ProductA[] storeA = { new ProductA { Name = "apple", Code = 9 }, 
                           new ProductA { Name = "orange", Code = 4 } };
    
    ProductA[] storeB = { new ProductA { Name = "apple", Code = 9 }, 
                           new ProductA { Name = "orange", Code = 4 } };
    
    bool equalAB = storeA.SequenceEqual(storeB);
    
    IEnumerable<Item> noduplicates = items.Distinct();