首页 > 解决方案 > 如何对具有相同子对象列表的对象进行分组?

问题描述

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

我知道这是问题所在,就好像我停止对列表进行分组一样,代码输出两项而不是三项。

如何调整我的表达方式,以便正确地将前两项(相同的)组合为一个?

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; }
}
var items = new List<Item> {
  new Item {
    Id = "00001",
    Name = "My Item",
    Children = new List<ChildItem> {
      new ChildItem { ChildItemName = "string 1" },
      new ChildItem { ChildItemName = "string 2" },
    }
  },
  new Item {
    Id = "00001",
    Name = "My Item",
    Children = new List<ChildItem> {
      new ChildItem { ChildItemName = "string 1" },
      new ChildItem { ChildItemName = "string 2" },
    }
  },
  new Item {
    Id = "00002",
    Name = "My Second Item",
    Children = new List<ChildItem> {
      new ChildItem { ChildItemName = "string 3" },
    }
  }
};

var result =
  from c in items
  group c by new
  {
    c.Id,
    c.Name,
    c.Children,
  }
  into gcs
  select new Item()
  {
    Id = gcs.Key.Id,
    Name = gcs.Key.Name,
    Children = gcs.Key.Children
  };

标签: c#listlinqgroup-by

解决方案


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

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

您不需要将所有内容都放入组密钥中。


由于根据您的评论,Id不保证唯一性,因此您必须提供自己对“相等”含义的定义。您可以通过实现自己的相等比较器来做到这一点。

as 单例的实现ChildItem,因为它将在ItemEqualityComparer.Equals.

class ChildItemEqualityComparer : IEqualityComparer<ChildItem>
{
    public static readonly ChildItemEqualityComparer Instance =
        new ChildItemEqualityComparer(); // Create singleton.
    private ChildItemEqualityComparer() { } // Hide constructor.

    public bool Equals(ChildItem x, ChildItem y) =>
        String.Equals(x.ChildItemName, y.ChildItemName);
    public int GetHashCode(ChildItem childItem) =>
        childItem.ChildItemName?.GetHashCode() ?? 0;
}

的实现Item

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;
    }
}

和查询:

var result = items.Distinct(new ItemEqualityComparer());

这会产生 2 个项目。


推荐阅读