How to get distinct instance from a list by Lambda or LINQ
我有一个这样的班级:
1 2 3 4 | class MyClass<T> { public string value1 { get; set; } public T objT { get; set; } } |
以及这个类的列表。我想使用.NET 3.5 lambda或linq按不同的值1获取MyClass的列表。我想这是可能的,而且比.NET 2.0中缓存类似列表的方法简单得多:
1 2 3 4 5 6 7 8 | List<MyClass<T>> list; ... List<MyClass<T>> listDistinct = new List<MyClass<T>>(); foreach (MyClass<T> instance in list) { // some code to check if listDistinct does contain obj with intance.Value1 // then listDistinct.Add(instance); } |
lambda或linq的方法是什么?
马克和达尔比的答案似乎都很有效。不过,我有一个更简单的解决方案。不使用
1 2 3 4 5 | var listDistinct = list.GroupBy( i => i.value1, (key, group) => group.First() ).ToArray(); |
注意,我已经将两个函数传递给了
我用以下输入运行了一个测试:
1 2 3 4 5 6 7 8 9 10 11 | var list = new [] { new { value1 ="ABC", objT = 0 }, new { value1 ="ABC", objT = 1 }, new { value1 ="123", objT = 2 }, new { value1 ="123", objT = 3 }, new { value1 ="FOO", objT = 4 }, new { value1 ="BAR", objT = 5 }, new { value1 ="BAR", objT = 6 }, new { value1 ="BAR", objT = 7 }, new { value1 ="UGH", objT = 8 }, }; |
结果是:
1 2 3 4 5 | //{ value1 = ABC, objT = 0 } //{ value1 = 123, objT = 2 } //{ value1 = FOO, objT = 4 } //{ value1 = BAR, objT = 5 } //{ value1 = UGH, objT = 8 } |
我还没有测试它的性能。我相信这个解决方案可能比使用
隐马尔可夫模型。。。我可能会写一个自定义的
1 | var listDistinct = list.Distinct(comparer).ToList(); |
通过LINQ编写比较器……
可能有点杀伤力过大,但可重复使用,至少:
用法第一:
1 2 3 4 5 6 7 8 9 10 11 12 13 | static class Program { static void Main() { var data = new[] { new { Foo = 1,Bar ="a <div class="suo-content">[collapse title=""]<ul><li>关于代码的一个问题:什么是projectioncomparer?一个.NET类、LINQ或IEnumerable相关类,以便您具有自定义扩展名?</li><li>好啊。我认为"projectioncomparer"是您定义的任何类名,但是在类中,您已经将DistinctBy()扩展方法自定义为IEnumerable,而projectioncomparer<t>是另一个助手类,对吗?ProjectionComparer是否可以是不同的名称,而不是相同的名称?</li><li>如果我想得到MyClass的Value1列表,我可以这样使用这个比较器:list<string>listValue1s=list.distinct(comparer.tolist()。select(y=>y.value1);对吗?</li><li>ProjectionComparer的名称并不重要-您可以将其称为EnumerableExtensions。projection comparer<t>之所以如此命名,是因为它通过投影提供比较器,这是基于现有值(例如,MyClass中的Value1)获取新值的常用术语。最后一个问题是:除非需要,否则不要调用tolist()。如果您不打算使用myClass<t>对象的distinct列表,那么最好像这样获取值1:IEnumerable<string>value1 s=list.select(y=>y.value1).distinct();</li><li>谢谢Dahlbyk;帮我节省了一些打字时间;-p</li><li>马克,你对JPBOchi的替代方法有什么意见吗?似乎不需要编写一个比较器扩展类,而且非常灵活。对于linq-to-object来说,这似乎已经足够好了。</li><li>它们本质上是相同的,除了我的可以用于任何对象,而不仅仅是一个特定的案例。</li></ul>[/collapse]</div><p><center>[wp_ad_camp_1]</center></p><hr><P>您可以使用此扩展方法:</P>[cc lang="csharp"] IEnumerable<MyClass> distinctList = sourceList.DistinctBy(x => x.value1); public static IEnumerable<TSource> DistinctBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) { var knownKeys = new HashSet<TKey>(); return source.Where(element => knownKeys.Add(keySelector(element))); } |
Check out Enumerable.Distinct(), which can accept an IEqualityComparer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class MyClassComparer<T> : IEqualityComparer<MyClass<T>> { // Products are equal if their names and product numbers are equal. public bool Equals(MyClass<T> x, MyClass<T>y) { // Check whether the compared objects reference the same data. if (Object.ReferenceEquals(x, y)) return true; // Check whether any of the compared objects is null. if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) return false; // Check whether the products' properties are equal. return x.value1 == y.value1; } // If Equals() returns true for a pair of objects, // GetHashCode must return the same value for these objects. public int GetHashCode(MyClass<T> x) { // Check whether the object is null. if (Object.ReferenceEquals(x, null)) return 0; // Get the hash code for the Name field if it is not null. return (x.value1 ??"").GetHashCode(); } } |
您的代码段可能如下所示:
1 2 3 | List<MyClass<T>> list; ... List<MyClass<T>> listDistinct = list.Distinct(new MyClassComparer<T>).ToList(); |
在LINQ中,这是更先进的团队
1 | list.GroupBy(li => li.value, (key, grp) => li.FirstOrDefault()); |
这会更简单…
1 | var distinctList = list.GroupBy(l => l.value1, (key, c) => l.FirstOrDefault()); |
我接受了Marc的答案,将其修复为使用值类型的tsource(默认测试(tsource)而不是空值),清理了一些冗余的类型规范,并为其编写了一些测试。这是我今天使用的。感谢马克的伟大思想和实施。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | public static class LINQExtensions { public static IEnumerable<TSource> DistinctBy<TSource, TValue>( this IEnumerable<TSource> source, Func<TSource, TValue> selector) { var comparer = ProjectionComparer<TSource>.CompareBy( selector, EqualityComparer<TValue>.Default); return new HashSet<TSource>(source, comparer); } } public static class ProjectionComparer<TSource> { public static IEqualityComparer<TSource> CompareBy<TValue>( Func<TSource, TValue> selector) { return CompareBy(selector, EqualityComparer<TValue>.Default); } public static IEqualityComparer<TSource> CompareBy<TValue>( Func<TSource, TValue> selector, IEqualityComparer<TValue> comparer) { return new ComparerImpl<TValue>(selector, comparer); } sealed class ComparerImpl<TValue> : IEqualityComparer<TSource> { private readonly Func<TSource, TValue> _selector; private readonly IEqualityComparer<TValue> _comparer; public ComparerImpl( Func<TSource, TValue> selector, IEqualityComparer<TValue> comparer) { if (selector == null) throw new ArgumentNullException("selector"); if (comparer == null) throw new ArgumentNullException("comparer"); _selector = selector; _comparer = comparer; } bool IEqualityComparer<TSource>.Equals(TSource x, TSource y) { if (x.Equals(default(TSource)) && y.Equals(default(TSource))) { return true; } if (x.Equals(default(TSource)) || y.Equals(default(TSource))) { return false; } return _comparer.Equals(_selector(x), _selector(y)); } int IEqualityComparer<TSource>.GetHashCode(TSource obj) { return obj.Equals(default(TSource)) ? 0 : _comparer.GetHashCode(_selector(obj)); } } } |
测试班:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | [TestClass] public class LINQExtensionsTest { [TestMethod] public void DistinctByTestDate() { var list = Enumerable.Range(0, 200).Select(i => new { Index = i, Date = DateTime.Today.AddDays(i%4) }).ToList(); var distinctList = list.DistinctBy(l => l.Date).ToList(); Assert.AreEqual(4, distinctList.Count); Assert.AreEqual(0, distinctList[0].Index); Assert.AreEqual(1, distinctList[1].Index); Assert.AreEqual(2, distinctList[2].Index); Assert.AreEqual(3, distinctList[3].Index); Assert.AreEqual(DateTime.Today, distinctList[0].Date); Assert.AreEqual(DateTime.Today.AddDays(1), distinctList[1].Date); Assert.AreEqual(DateTime.Today.AddDays(2), distinctList[2].Date); Assert.AreEqual(DateTime.Today.AddDays(3), distinctList[3].Date); Assert.AreEqual(200, list.Count); } [TestMethod] public void DistinctByTestInt() { var list = Enumerable.Range(0, 200).Select(i => new { Index = i % 4, Date = DateTime.Today.AddDays(i) }).ToList(); var distinctList = list.DistinctBy(l => l.Index).ToList(); Assert.AreEqual(4, distinctList.Count); Assert.AreEqual(0, distinctList[0].Index); Assert.AreEqual(1, distinctList[1].Index); Assert.AreEqual(2, distinctList[2].Index); Assert.AreEqual(3, distinctList[3].Index); Assert.AreEqual(DateTime.Today, distinctList[0].Date); Assert.AreEqual(DateTime.Today.AddDays(1), distinctList[1].Date); Assert.AreEqual(DateTime.Today.AddDays(2), distinctList[2].Date); Assert.AreEqual(DateTime.Today.AddDays(3), distinctList[3].Date); Assert.AreEqual(200, list.Count); } struct EqualityTester { public readonly int Index; public readonly DateTime Date; public EqualityTester(int index, DateTime date) : this() { Index = index; Date = date; } } [TestMethod] public void TestStruct() { var list = Enumerable.Range(0, 200) .Select(i => new EqualityTester(i, DateTime.Today.AddDays(i%4))) .ToList(); var distinctDateList = list.DistinctBy(e => e.Date).ToList(); var distinctIntList = list.DistinctBy(e => e.Index).ToList(); Assert.AreEqual(4, distinctDateList.Count); Assert.AreEqual(200, distinctIntList.Count); } } |