代码之家  ›  专栏  ›  技术社区  ›  mheptinstall

使用LINQ GroupBy获取唯一集合时忽略属性

  •  1
  • mheptinstall  · 技术社区  · 7 年前

    Rules 我正在尝试创建另一个 规则 Site 属性并创建唯一列表。

    public class Rule
    {
        public int TestId { get; set; }
        public string File { get; set; }
        public string Site { get; set; }
        public string[] Columns { get; set; }
    }
    

    如果我的收藏有如下价值:

    var rules = new List<Rule>
    {
        new Rule { TestId = 1, File = "Foo", Site = "SiteA", Columns = new string[] { "ColA", "ColB" }},
        new Rule { TestId = 1, File = "Foo", Site = "SiteB", Columns = new string[] { "ColA", "ColB" }}
    };
    

    var uniqueRules = new List<Rule>
    {
        new Rule { TestId = 1, File = "Foo", Site = null, Columns = new string[] { "ColA", "ColB" }}
    };
    

    在尝试了下面的各种组合之后,我仍然得到了两个结果,我如何达到预期的结果?

    var uniqueRules = rules
        .GroupBy(r => new { r.TestId, r.File, r.Columns })
        .Select(g => g.Key)
        .Distinct()
        .ToList();
    
    4 回复  |  直到 7 年前
        1
  •  6
  •   Tim Schmelter    7 年前

    问题是 string[] 尚未覆盖 Equals GetHashCode ,这就是为什么只在 r.Columns . 你需要提供一个自定义 IEqualityComparer<T>

    public class RuleComparer : IEqualityComparer<Rule>
    {
        public bool Equals(Rule x, Rule y)
        {
            if (object.ReferenceEquals(x, y)) return true;
            if (x == null || y == null) return false;
            if(!(x.TestId == y.TestId && x.File == y.File)) return false;
            return x.Columns.SequenceEqual(y.Columns);
        }
    
        // from: https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
        public int GetHashCode(Rule obj)
        {
            unchecked
            {
                int hash = 17;
                hash = hash * 23 + obj.TestId.GetHashCode();
                hash = hash * 23 + (obj.File?.GetHashCode() ?? 0);
                foreach(string s in obj.Columns)
                    hash = hash * 23 + (s?.GetHashCode() ?? 0);
                return hash;
            }
        }
    }
    

    现在LINQ查询变得很简单:

    List<Rule> uniqueRules = rules.Distinct(new RuleComparer()).ToList();
    
        2
  •  1
  •   ThoNohT    7 年前

    这里有几点意见:

    1. GroupBy() 会有和做同样的效果 Distinct() . 所以要么创建一个 EqualityComparer 它将为您执行比较,或者 ,无需两者兼备。

    2. Key .First() 如果你想要一个真正的 Rule ,如果多个在同一个分组中,则不在乎是哪一个。

    3. 规则是不同的,因为 Columns

    要组合所有这些观察结果,如果您不想编写自定义代码,可以使用以下代码 均衡器 按分组方式:

    var uniqueRules = rules
            .GroupBy(r => new { r.TestId, r.File, Columns = string.Join(",", r.Columns) })
            .Select(r => r.First())
            .ToList();
    

    这将简单地为列使用一个字符串,使其成为一个也可以通过值进行比较的属性。

    请注意,这只可能是因为

        3
  •  0
  •   PiJei    7 年前

    我建议您扩展您的类规则,实现如下equals方法:

    public class Rule :IEquatable<Rule>
        {
            public int TestId { get; set; }
            public string File { get; set; }
            public string Site { get; set; }
            public string[] Columns { get; set; }
    
            public bool Equals(Rule other)
            {
                return TestId == other.TestId &&
                       string.Equals(File, other.File) &&
                       Equals(Columns, other.Columns);
            }
        }
    

    如您所见,在比较这两个类时,我们忽略了Site字段。这也给了你在将来改变比较的灵活性。 然后使用: rules.Distinct();

        4
  •  0
  •   Ashkan Mobayen Khiabani    7 年前

    new string[] { "ColA", "ColB" } 但是这些对象没有引用同一个对象,它们只有相等的数据。试试这个:

    string[] cols = new string[] { "ColA", "ColB" };
    var rules = new List<Rule>
    {
        new Rule { TestId = 1, File = "Foo", Site = "SiteA", Columns = cols},
        new Rule { TestId = 1, File = "Foo", Site = "SiteB", Columns = cols}
    };
    

    var uniqueRules = rules
        .GroupBy(r => new { r.TestId, r.File, r.Columns })
        .Select(g => g.Key)
        .Distinct()
        .ToList();