如何基于含IngredientsIds的所有属性实现Recipe类的DistinctBy去重
解决Recipe类的去重问题
你的代码无法正确去重,核心原因有两个:
- 匿名类中的
IEnumerable<int>类型(也就是IngredientsIds)默认用引用比较,而非内容比较——哪怕两个集合的元素完全一致,只要是不同的集合实例,就会被判定为不同的键。 - 你需要忽略
IngredientsIds的顺序,只判断元素是否完全相同,这也不在默认的去重逻辑范围内。
下面给你两种可行的解决方案:
方案一:自定义相等比较器(更严谨)
创建一个实现IEqualityComparer<Recipe>的比较器类,逐一对比所有简单属性,再通过SetEquals判断IngredientsIds的元素是否一致(因为你说元素不会重复,SetEquals可以忽略顺序直接判断内容是否完全匹配)。
public class RecipeEqualityComparer : IEqualityComparer<Recipe> { public bool Equals(Recipe x, Recipe y) { if (ReferenceEquals(x, y)) return true; if (x == null || y == null) return false; // 逐一对比所有简单类型属性 if (x.Name != y.Name) return false; if (x.Cost != y.Cost) return false; // 这里补充你那10个其他简单属性的对比,比如: // if (x.PrepTime != y.PrepTime) return false; // ... // 对比IngredientsIds:转成HashSet后用SetEquals忽略顺序判断内容 var xIngredients = x.IngredientsIds?.ToHashSet() ?? new HashSet<int>(); var yIngredients = y.IngredientsIds?.ToHashSet() ?? new HashSet<int>(); return xIngredients.SetEquals(yIngredients); } public int GetHashCode(Recipe obj) { if (obj == null) return 0; var hash = new HashCode(); // 组合所有简单属性的哈希码 hash.Add(obj.Name); hash.Add(obj.Cost); // 补充你那10个其他属性的哈希码: // hash.Add(obj.PrepTime); // ... // 组合IngredientsIds的哈希码:先排序再逐个添加,确保顺序不影响结果 if (obj.IngredientsIds != null) { foreach (var id in obj.IngredientsIds.OrderBy(id => id)) { hash.Add(id); } } return hash.ToHashCode(); } }
使用时直接把比较器传给Distinct方法:
var uniqueRecipes = recipes.Distinct(new RecipeEqualityComparer());
方案二:修改DistinctBy的键(更简洁)
把IngredientsIds转换成排序后的字符串,这样匿名类的键会基于内容判断相等,同时自动处理顺序问题。
var uniqueRecipes = recipes .DistinctBy(d => new { d.Name, d.Cost, // 这里补充你那10个其他简单属性 // d.PrepTime, // ... // 将IngredientsIds排序后拼接成字符串,确保元素相同则键相同 IngredientsKey = d.IngredientsIds != null ? string.Join(",", d.IngredientsIds.OrderBy(id => id)) : string.Empty });
这种方式适合简单场景,缺点是如果IngredientsIds的元素数量很大,拼接字符串可能有轻微的性能损耗,但大部分日常场景下完全够用。
内容的提问来源于stack exchange,提问作者asdgvx
相关产品推荐
相关产品推荐

