两个列表之间的差异
本文关键字:之间 列表 两个 | 更新日期: 2023-09-27 17:49:43
我有两个用CustomsObject填充的泛型列表。
我需要在第三个列表中检索这两个列表之间的差异(第一个列表中没有第二个列表中项目的项目(。
我认为使用.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));
我认为需要强调的是,使用Except方法将返回第一个中的项,而只返回第二个中的项目。它不会返回在second中并没有出现在first中的元素。
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
但如果你想得到两个列表之间的真正区别:
第一个项目中没有第二个项目的项目以及在没有第一个项目的情况下在第二个项目中的项目。
你需要使用除了两次:
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()
。(
如果被比较的值是基本数据类型,例如int
、string
、decimal
等,则Except()
方法使用默认比较器来比较这些值。
否则,将根据对象地址进行比较,这可能不是您想要的。。。在这种情况下,让您的自定义对象实现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;
}
要从两个列表中获得唯一的差异,可以将它们合并(并集(,除了两个列表内相同的值(交集(,例如:
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扩展方法对两个IEumerable进行操作,在我看来,它将是一个O(n^2(操作。如果性能有问题(如果说您的列表很大(,我建议从list1创建一个HashSet,并使用HashSet的ExceptWith方法。
这是我的解决方案:
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
在上面提到的代码中,我得到了基类和派生类列表之间的属性差异
var resultList = checklist.Where(p => myList.All(l => p.value != l.value)).ToList();
List<ObjectC> _list_DF_BW_ANB = new List<ObjectC>();
List<ObjectA> _listA = new List<ObjectA>();
List<ObjectB> _listB = new List<ObjectB>();
foreach (var itemB in _listB )
{
var flat = 0;
foreach(var itemA in _listA )
{
if(itemA.ProductId==itemB.ProductId)
{
flat = 1;
break;
}
}
if (flat == 0)
{
_list_DF_BW_ANB.Add(itemB);
}
}
如果两个列表都实现了IEnumerable接口,则可以使用LINQ实现这一点。
list3 = list1.where(i => !list2.contains(i));
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
List<int> listDifference = new List<int>();
foreach (var item1 in list1)
{
foreach (var item2 in list2)
{
if (item1 != item2)
listDifference.Add(item1);
}
}