我有两个填充有CustomsObjects的通用列表。
我需要在第三个列表中检索这两个列表之间的差异(第一个列表中存在但第二个列表中不存在的项目)。
我正在考虑使用.Except()
是一个好主意,但我不知道如何使用它。帮帮我!
我有两个填充有CustomsObjects的通用列表。
我需要在第三个列表中检索这两个列表之间的差异(第一个列表中存在但第二个列表中不存在的项目)。
我正在考虑使用.Except()
是一个好主意,但我不知道如何使用它。帮帮我!
使用Except
恰好是正确的方法。如果你的类型覆盖了Equals
和GetHashCode
,或者你只关心引用类型的相等性(即只有两个引用指向完全相同的对象时才“相等”),那么你可以直接使用:
var list3 = list1.Except(list2).ToList();
如果您需要按照自定义标识(例如ID)来表达相等的概念,则需要实现IEqualityComparer<T>
接口。例如:
public class IdComparer : IEqualityComparer<CustomObject>
{
public int GetHashCode(CustomObject co)
{
if (co == null)
{
return 0;
}
return co.Id.GetHashCode();
}
public bool Equals(CustomObject x1, CustomObject x2)
{
if (object.ReferenceEquals(x1, x2))
{
return true;
}
if (object.ReferenceEquals(x1, null) ||
object.ReferenceEquals(x2, null))
{
return false;
}
return x1.Id == x2.Id;
}
}
然后使用:
var list3 = list1.Except(list2, new IdComparer()).ToList();
请注意,这将删除任何重复的元素。如果您需要保留重复项,最简单的方法可能是从list2
创建一个集合并使用类似以下的代码:
var list3 = list1.Where(x => !set2.Contains(x)).ToList();
你可以做这样的事情:
var result = customlist.Where(p => !otherlist.Any(l => p.someproperty == l.someproperty));
Any
应该改成All
。因为第二个列表中可能会有来自第一个列表的项目,但如果不是第一个被检查的项目,则它将立即获得p.someproperty != l.someproperty
的 true。这导致返回存在于两个列表中的项。对那6个点赞的人感到遗憾。 - Murphybro2我认为需要强调的是 - 使用Except方法将只返回第一个序列中不包含在第二个序列中的项。它不会返回第二个序列中不在第一个序列中出现的元素。
var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };
var list3 = list1.Except(list2).ToList(); //list3 contains only 1, 2
但是,如果你想要获取两个列表之间的真正差异:
第一个列表中有而第二个列表中没有的项目以及第二个列表中有而第一个列表中没有的项目。
你需要使用Except函数两次:
var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };
var list3 = list1.Except(list2); //list3 contains only 1, 2
var list4 = list2.Except(list1); //list4 contains only 6, 7
var resultList = list3.Concat(list4).ToList(); //resultList contains 1, 2, 6, 7
或者您可以使用 HashSet 的 SymmetricExceptWith 方法。但是它会更改被调用的集合:
var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };
var list1Set = list1.ToHashSet(); //.net framework 4.7.2 and .net core 2.0 and above otherwise new HashSet(list1)
list1Set.SymmetricExceptWith(list2);
var resultList = list1Set.ToList(); //resultList contains 1, 2, 6, 7
var list3 = list1.Where(x => !list2.Any(z => z.Id == x.Id)).ToList();
注意:list3
将包含两个列表中不同的项目或对象。
注意:正确写法是 ToList()
而不是 toList()
var third = first.Except(second);
如果您不喜欢引用延迟加载的集合,还可以在Except()
之后调用ToList()
。
Except()
方法将使用默认比较器比较值,如果被比较的值是基本数据类型,如int
、string
、decimal
等。
否则,比较将通过对象地址进行,这可能不是您想要的... 在这种情况下,让您的自定义对象实现IComparable
(或实现自定义IEqualityComparer
并将其传递给Except()
方法)。
oldValues
,远程集合称为newValues
。
不时会收到关于远程集合中的一些元素已更改的通知,并且您想知道添加了哪些元素,删除了哪些元素以及更新了哪些元素。 远程集合始终返回其拥有的所有元素。 public class ChangesTracker<T1, T2>
{
private readonly IEnumerable<T1> oldValues;
private readonly IEnumerable<T2> newValues;
private readonly Func<T1, T2, bool> areEqual;
public ChangesTracker(IEnumerable<T1> oldValues, IEnumerable<T2> newValues, Func<T1, T2, bool> areEqual)
{
this.oldValues = oldValues;
this.newValues = newValues;
this.areEqual = areEqual;
}
public IEnumerable<T2> AddedItems
{
get => newValues.Where(n => oldValues.All(o => !areEqual(o, n)));
}
public IEnumerable<T1> RemovedItems
{
get => oldValues.Where(n => newValues.All(o => !areEqual(n, o)));
}
public IEnumerable<T1> UpdatedItems
{
get => oldValues.Where(n => newValues.Any(o => areEqual(n, o)));
}
}
使用方法
[Test]
public void AddRemoveAndUpdate()
{
// Arrange
var listA = ChangesTrackerMockups.GetAList(10); // ids 1-10
var listB = ChangesTrackerMockups.GetBList(11) // ids 1-11
.Where(b => b.Iddd != 7); // Exclude element means it will be delete
var changesTracker = new ChangesTracker<A, B>(listA, listB, AreEqual);
// Assert
Assert.AreEqual(1, changesTracker.AddedItems.Count()); // b.id = 11
Assert.AreEqual(1, changesTracker.RemovedItems.Count()); // b.id = 7
Assert.AreEqual(9, changesTracker.UpdatedItems.Count()); // all a.id == b.iddd
}
private bool AreEqual(A a, B b)
{
if (a == null && b == null)
return true;
if (a == null || b == null)
return false;
return a.Id == b.Iddd;
}
要从两个列表中获取唯一的差异,您可以将它们合并(Union),但同时排除这两个列表中相同的值(Intersect),例如:
var list1 = new List<int> { 1, 2, 3, 4, 5 };
var list2 = new List<int> { 3, 4, 5, 6, 7 };
var diffs = list1.Union(list2).Except(list1.Intersect(list2));
对于复杂类型,如果您始终使用相同的比较模式来比较实例,则应实现IComparable。如果在某些情况下需要不同的比较模式,则可以始终创建实现IEqualityComparer的类。
由于 Except 扩展方法作用于两个 IEumerables,我认为它将是一个 O(n^2) 的操作。如果性能是一个问题(比如说你的列表很大),我建议从 list1 创建一个 HashSet,并使用 HashSet 的 ExceptWith 方法。
Enumerable.Except
内部使用 HashSet
或类似的东西。它绝对不使用简单粗暴的 O(n^2) 算法。 - Jim Mischel这是我的解决方案:
List<String> list1 = new List<String>();
List<String> list2 = new List<String>();
List<String> exceptValue = new List<String>();
foreach(String L1 in List1)
{
if(!List2.Contains(L1)
{
exceptValue.Add(L1);
}
}
foreach(String L2 in List2)
{
if(!List1.Contains(L2)
{
exceptValue.Add(L2);
}
}
有点晚了,但这对我来说是可行的解决方案
var myBaseProperty = (typeof(BaseClass)).GetProperties();//get base code properties
var allProperty = entity.GetProperties()[0].DeclaringType.GetProperties();//get derived class property plus base code as it is derived from it
var declaredClassProperties = allProperty.Where(x => !myBaseProperty.Any(l => l.Name == x.Name)).ToList();//get the difference
Except
是一个集合操作符,因此结果列表中的值将是不同的,例如{'A','A','B','C'}. Except({'B','C'})
将返回{'A'}
。 - digEmAll