比较两个嵌套列表列表并返回 added/changed/removed 项
Comparing two lists of nested lists and returning the added/changed/removed items
我在 Whosebug 上看过很多类似的问题,但没有看到与我的问题完全匹配的问题。
我需要比较两个 "lists of nested lists" 并找出差异。一个是 "old" 列表,另一个是 "new" 列表。比较嵌套列表时,如果所有嵌套列表项(MyObject.Ids)按顺序出现在两个列表中(您可以假设嵌套 MyObject.Ids 列表已经排序并且没有重复)。 MyObject.Id 和 MyObject.Name 属性不在相等比较中考虑,但它们仍然是 MyObject 的重要元数据,不应丢失。
我不是在寻找平等的布尔指标。相反,我需要创建三个新列表来捕获旧列表和新列表之间的差异(例如,添加的项目列表、删除的项目列表以及两个列表中都存在的项目列表)。
下面是一些完全符合我要求的代码示例!我想知道的是如何制作这个 shorter/better/simpler(去掉其中一个 for 循环将是一个好的开始)。为了使事情变得更棘手,请假设您不能对 MyObject class 进行任何更改或使用任何自定义 Equals/IEqualityComparer 等实现。
public class MyObject
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Guid> Ids { get; set; }
}
...
// Get the list of existing objects (assume this returns some populated list)
List<MyObject> existingObjects = GetExistingObjects();
// Create a list of updated objects
List<MyObject> updatedObjects = new List<MyObject>()
{
new MyObject()
{
Ids = new List<Guid>() { new Guid("48af3cb9-945a-4ab9-91e4-7ee5765e5304"), new Guid("54b5128a-cf53-436c-9d88-2ef7abd15140") }
},
new MyObject()
{
Ids = new List<Guid>() { new Guid("0485382f-8f92-4a71-9eba-09831392ceb9"), new Guid("3d8b98df-caee-41ce-b802-2f0c5f9742de") }
}
};
// Do the comparison and capture the differences
List<MyObject> addedObjects = new List<MyObject>();
List<MyObject> removedObjects = new List<MyObject>();
List<MyObject> sameObjects = new List<MyObject>();
foreach (MyObject obj in updatedObjects)
{
if (existingObjects.Any(list => list.Ids.SequenceEqual(obj.Ids)))
{
sameObjects.Add(obj);
continue;
}
addedObjects.Add(obj);
}
foreach (MyObject obj in existingObjects)
{
if (!updatedObjects.Any(list => list.Ids.SequenceEqual(obj.Ids)))
{
removedObjects.Add(obj);
}
}
您可以在 Linq 中使用 Intersect 和 Except 函数
使用 Intersect 你会得到现有的对象,
使用 Except 你会得到新的对象。
MSDN 除外示例:
double[] numbers1 = { 2.0, 2.1, 2.2, 2.3, 2.4, 2.5 };
double[] numbers2 = { 2.2 };
IEnumerable<double> onlyInFirstSet = numbers1.Except(numbers2);
foreach (double number in onlyInFirstSet)
Console.WriteLine(number);
这里更短一些(由于消除了第二个循环)并且更好一些(由于消除了第二个循环中包含的无效搜索)。由于循环中包含的无效搜索,仍然是 O(N^2) 时间复杂度。
var addedObjects = new List<MyObject>();
var removedObjects = new List<MyObject>(existingObjects);
var sameObjects = new List<MyObject>();
foreach (var newObject in updatedObjects)
{
int index = removedObjects.FindIndex(oldObject => oldObject.Ids.SequenceEqual(newObject.Ids));
if (index < 0)
addedObjects.Add(newObject);
else
{
removedObjects.RemoveAt(index);
sameObjects.Add(newObject);
}
}
更新: 一个更短的,但 IMO 绝对不是更好(实际上更差的性能)版本
var addedObjects = updatedObjects.Where(newObject => !existingObjects.Any(oldObject => oldObject.Ids.SequenceEqual(newObject.Ids))).ToList();
var removedObjects = existingObjects.Where(oldObject => !updatedObjects.Any(newObject => newObject.Ids.SequenceEqual(oldObject.Ids))).ToList();
var sameObjects = updatedObjects.Where(newObject => !addedObjects.Any(addedObject => addedObject.Ids.SequenceEqual(newObject.Ids))).ToList();
如果 MyObject
没有定义自定义相等比较,即使用默认引用相等,最后一行可以替换为更短且性能更好的
var sameObjects = updatedObjects.Except(addedObjects);
我在 Whosebug 上看过很多类似的问题,但没有看到与我的问题完全匹配的问题。
我需要比较两个 "lists of nested lists" 并找出差异。一个是 "old" 列表,另一个是 "new" 列表。比较嵌套列表时,如果所有嵌套列表项(MyObject.Ids)按顺序出现在两个列表中(您可以假设嵌套 MyObject.Ids 列表已经排序并且没有重复)。 MyObject.Id 和 MyObject.Name 属性不在相等比较中考虑,但它们仍然是 MyObject 的重要元数据,不应丢失。
我不是在寻找平等的布尔指标。相反,我需要创建三个新列表来捕获旧列表和新列表之间的差异(例如,添加的项目列表、删除的项目列表以及两个列表中都存在的项目列表)。
下面是一些完全符合我要求的代码示例!我想知道的是如何制作这个 shorter/better/simpler(去掉其中一个 for 循环将是一个好的开始)。为了使事情变得更棘手,请假设您不能对 MyObject class 进行任何更改或使用任何自定义 Equals/IEqualityComparer 等实现。
public class MyObject
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Guid> Ids { get; set; }
}
...
// Get the list of existing objects (assume this returns some populated list)
List<MyObject> existingObjects = GetExistingObjects();
// Create a list of updated objects
List<MyObject> updatedObjects = new List<MyObject>()
{
new MyObject()
{
Ids = new List<Guid>() { new Guid("48af3cb9-945a-4ab9-91e4-7ee5765e5304"), new Guid("54b5128a-cf53-436c-9d88-2ef7abd15140") }
},
new MyObject()
{
Ids = new List<Guid>() { new Guid("0485382f-8f92-4a71-9eba-09831392ceb9"), new Guid("3d8b98df-caee-41ce-b802-2f0c5f9742de") }
}
};
// Do the comparison and capture the differences
List<MyObject> addedObjects = new List<MyObject>();
List<MyObject> removedObjects = new List<MyObject>();
List<MyObject> sameObjects = new List<MyObject>();
foreach (MyObject obj in updatedObjects)
{
if (existingObjects.Any(list => list.Ids.SequenceEqual(obj.Ids)))
{
sameObjects.Add(obj);
continue;
}
addedObjects.Add(obj);
}
foreach (MyObject obj in existingObjects)
{
if (!updatedObjects.Any(list => list.Ids.SequenceEqual(obj.Ids)))
{
removedObjects.Add(obj);
}
}
您可以在 Linq 中使用 Intersect 和 Except 函数
使用 Intersect 你会得到现有的对象,
使用 Except 你会得到新的对象。
MSDN 除外示例:
double[] numbers1 = { 2.0, 2.1, 2.2, 2.3, 2.4, 2.5 };
double[] numbers2 = { 2.2 };
IEnumerable<double> onlyInFirstSet = numbers1.Except(numbers2);
foreach (double number in onlyInFirstSet)
Console.WriteLine(number);
这里更短一些(由于消除了第二个循环)并且更好一些(由于消除了第二个循环中包含的无效搜索)。由于循环中包含的无效搜索,仍然是 O(N^2) 时间复杂度。
var addedObjects = new List<MyObject>();
var removedObjects = new List<MyObject>(existingObjects);
var sameObjects = new List<MyObject>();
foreach (var newObject in updatedObjects)
{
int index = removedObjects.FindIndex(oldObject => oldObject.Ids.SequenceEqual(newObject.Ids));
if (index < 0)
addedObjects.Add(newObject);
else
{
removedObjects.RemoveAt(index);
sameObjects.Add(newObject);
}
}
更新: 一个更短的,但 IMO 绝对不是更好(实际上更差的性能)版本
var addedObjects = updatedObjects.Where(newObject => !existingObjects.Any(oldObject => oldObject.Ids.SequenceEqual(newObject.Ids))).ToList();
var removedObjects = existingObjects.Where(oldObject => !updatedObjects.Any(newObject => newObject.Ids.SequenceEqual(oldObject.Ids))).ToList();
var sameObjects = updatedObjects.Where(newObject => !addedObjects.Any(addedObject => addedObject.Ids.SequenceEqual(newObject.Ids))).ToList();
如果 MyObject
没有定义自定义相等比较,即使用默认引用相等,最后一行可以替换为更短且性能更好的
var sameObjects = updatedObjects.Except(addedObjects);