假设我们有2个集合,至少IEnumerable可以启动Linq(也假设.net 4.0):
List<T1> list1;
List<T2> list2;
我可以定义T1和T2类型的对象之间的相等性。
验证2个列表是否相等(元素的顺序不相关)的最佳方法(即.net接口和Linq首选)是什么。
如果我知道对象T1和T2有ID,我该如何优化此问题
T1和T2之前:
class Device
{
string Id;
string Name;
}
class DeviceInfo
{
string Identifier;
string FriendlyName;
DateTime CreateDate;
}
稍后编辑:
解决方案应该包含我编写的某种等式比较器,并且足够通用。可能存在2个对象具有相同Id但名称不同的情况,因此比较应该失败。例如:
static bool AreEqual(Device device, DeviceInfo deviceInfo)
{
return device.Id == deviceInfo.Identifier &&
device.Name == deviceInfo.FriendlyName;
}
答案 0 :(得分:3)
假设.NET 4.0:
Foo[] foos = new Foo[];
Bar[] bars = new Bar[];
var areDifferent = foos.Zip(bars, (foo, bar) => foo.Id == bar.Id).Any(b => !b);
更好的解决方案还会检查foos
和bars
是否具有相同的长度,并且所有元素都不是null
。当然,此示例假定集合已按Id
排序。
<强>更新强>
所以,这是LINQy所有细节中的“更好的解决方案”:
var areDifferent = foos.Count() != bars.Count() ||
foos.OrderBy(foo => foo.Id)
.Zip(
bars.OrderBy(bar => bar.Id),
(foo, bar) => foo != null && bar != null && foo.Id == bar.Id)
.Any(b => !b);
答案 1 :(得分:2)
你可以这样做:
List<Device> devices = ...
List<DeviceInfo> deviceInfos = ...
var deviceIds = devices.Select(d => d.Id)
.OrderBy(id => id);
var deviceInfoIds = deviceInfos.Select(d => d.Identifier)
.OrderBy(id => id);
bool areEqual = deviceIds.SequenceEqual(deviceInfoIds);
如果无法复制ID,设置语义将派上用场:
bool areEqual = !devices.Select(d => d.Id)
.Except(deviceInfos.Select(d => d.Identifier))
.Any();
如果可能的话,我建议您声明一个IHasId
(或类似的)接口并获取两种类型来实现它。
修改强>:
为了响应您的编辑,您可以编写一个执行您想要的IEqualityComparer
实现。它看起来真的很难看;你必须从每个参数到DeviceInfo / Device进行推测性转换,以尝试提取标识符。我不会真的推荐这个;平等比较器比较完全不同类型的对象是个坏主意。如果您使用每种类型来实现提供标识符的公共接口,那么它将更容易 lot 。
答案 2 :(得分:0)
比较2个字符串列表并不是很复杂。基于对列表排序的两种解决方案都具有N log(N)复杂度,而不考虑字符串的大小。更好的解决方案是(伪代码),复杂度为N:
create a dictionary<string, int>
foreach element in list1
if element is in dict
dict[element]++;
else
dict[element] = 1;
foreach element in list2
if element is in dict
dict[element]--;
else
return NOT_EQUAL;
if dict has only 0 values lists are equal
答案 3 :(得分:0)
试试这个来区分两个不同的列表: 如果他们有任何共同财产。
var differentItems = List<Type1>.Select(d => d.Name)
.Except(List<Type2>.Select(d => d.Name));