证明两个列表(包含对象)的内容相等的最简单方法
Simplest method to prove that the contents of two lists (containing objects) are equal
我在寻找一种简单的方法来比较和证明两个列表的内容相等时遇到了一些挫折。我看过很多关于 Whosebug 的解决方案,但都没有成功。一些解决方案看起来需要大量的工作来实施和做一些在我看来应该更简单的事情,但也许我太简单了以至于无法意识到这不能简单地完成:)
我创建了一个 fiddle,其中包含一些可以在此处查看的详细信息:https://dotnetfiddle.net/cvQr5d
或者,请在下面找到完整示例,我在对象比较方法(变量 finalResult
)上遇到问题,因为它返回 false
,如果正在比较内容,我会期望值成为 true
:
using System;
using System.Collections.Generic;
using System.Linq;
public class ResponseExample
{
public Guid Id { get; set; } = Guid.Parse("00000000-0000-0000-0000-000000000000");
public int Value { get; set; } = 0;
public string Initials { get; set; } = "J";
public string FirstName { get; set; } = "Joe";
public string Surname { get; set; } = "Blogs";
public string CellPhone { get; set; } = "0923232199";
public bool EmailVerified { get; set; } = false;
public bool CellPhoneVerified { get; set; } = true;
}
public class Program
{
public static void Main()
{
var responseOne = new ResponseExample();
var responseTwo = new ResponseExample();
var responseThree = new ResponseExample();
var responseFour = new ResponseExample();
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseThree);
objectListTwo.Add(responseFour);
bool result = objectListOne.Count == objectListTwo.Count();
Console.WriteLine($"Count: {result}");
bool finalResult = ScrambledEquals<ResponseExample>(objectListOne, objectListTwo);
Console.WriteLine($"Object compare: {finalResult}");
}
//
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T,
int>();
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
}
使用Enumerable.All<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) Method
判断一个序列的所有元素是否都满足条件
一旦你初始化了你的两个列表
list1.All(x=>list2.Contains(x))
这是通过确保 list2 中的所有元素都包含在 list1 中来实现的,否则 returns false
如果 2 个列表包含相同的对象,您的方法将按原样进行比较。所以它是 returning false 因为有 4 个不同的对象。如果您像这样创建列表,使用相同的对象,它将 return true:
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseTwo);
objectListTwo.Add(responseOne);
要在对象的内容相同时获得真值,您可以将对象序列化为 json 字符串,如下所示:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
JavaScriptSerializer json = new JavaScriptSerializer();
var cnt = new Dictionary<string,
int>();
foreach (T _s in list1)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T _s in list2)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
正如评论中的人所指出的那样,这将不起作用,因为默认情况下比较复杂类型会比较 reference 是否相同。如果不实现相等方法(然后您将需要重载 GetHashCode 等),逐个字段比较将无法工作。参见 https://docs.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0
但是,如果您可以使用 fiddle 中的 c# 9,则可以将类型定义为 record
而不是 class
。记录已建立在逐场比较中。参见 https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#characteristics-of-records
因此 public class ResponseExample
将变为 public record ResponseExample
,您的代码将按预期工作。
如果性能不是什么大问题,可以使用Newtonsoft.Json。我们将能够比较不同类型的对象以及 运行 深度相等检查。
首先安装包:
Install-Package Newtonsoft.Json
这是代码片段:
public static bool DeepEqualsUsingJson<T>(IList<T> l1, IList<T> l2)
{
if (ReferenceEquals(l1, l2))
return true;
if (ReferenceEquals(l2, null))
return false;
if (l1.Count != l2.Count)
return false;
var l1JObject = l1.Select(i => JObject.FromObject(i)).ToList();
var l2JObject = l2.Select(i => JObject.FromObject(i)).ToList();
foreach (var o1 in l1JObject)
{
var index = l2JObject.FindIndex(o2 => JToken.DeepEquals(o1, o2));
if (index == -1)
return false;
l2JObject.RemoveAt(index);
}
return l2JObject.Count == 0;
}
我在寻找一种简单的方法来比较和证明两个列表的内容相等时遇到了一些挫折。我看过很多关于 Whosebug 的解决方案,但都没有成功。一些解决方案看起来需要大量的工作来实施和做一些在我看来应该更简单的事情,但也许我太简单了以至于无法意识到这不能简单地完成:)
我创建了一个 fiddle,其中包含一些可以在此处查看的详细信息:https://dotnetfiddle.net/cvQr5d
或者,请在下面找到完整示例,我在对象比较方法(变量 finalResult
)上遇到问题,因为它返回 false
,如果正在比较内容,我会期望值成为 true
:
using System;
using System.Collections.Generic;
using System.Linq;
public class ResponseExample
{
public Guid Id { get; set; } = Guid.Parse("00000000-0000-0000-0000-000000000000");
public int Value { get; set; } = 0;
public string Initials { get; set; } = "J";
public string FirstName { get; set; } = "Joe";
public string Surname { get; set; } = "Blogs";
public string CellPhone { get; set; } = "0923232199";
public bool EmailVerified { get; set; } = false;
public bool CellPhoneVerified { get; set; } = true;
}
public class Program
{
public static void Main()
{
var responseOne = new ResponseExample();
var responseTwo = new ResponseExample();
var responseThree = new ResponseExample();
var responseFour = new ResponseExample();
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseThree);
objectListTwo.Add(responseFour);
bool result = objectListOne.Count == objectListTwo.Count();
Console.WriteLine($"Count: {result}");
bool finalResult = ScrambledEquals<ResponseExample>(objectListOne, objectListTwo);
Console.WriteLine($"Object compare: {finalResult}");
}
//
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T,
int>();
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
}
使用Enumerable.All<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) Method
判断一个序列的所有元素是否都满足条件
一旦你初始化了你的两个列表
list1.All(x=>list2.Contains(x))
这是通过确保 list2 中的所有元素都包含在 list1 中来实现的,否则 returns false
如果 2 个列表包含相同的对象,您的方法将按原样进行比较。所以它是 returning false 因为有 4 个不同的对象。如果您像这样创建列表,使用相同的对象,它将 return true:
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseTwo);
objectListTwo.Add(responseOne);
要在对象的内容相同时获得真值,您可以将对象序列化为 json 字符串,如下所示:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
JavaScriptSerializer json = new JavaScriptSerializer();
var cnt = new Dictionary<string,
int>();
foreach (T _s in list1)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T _s in list2)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
正如评论中的人所指出的那样,这将不起作用,因为默认情况下比较复杂类型会比较 reference 是否相同。如果不实现相等方法(然后您将需要重载 GetHashCode 等),逐个字段比较将无法工作。参见 https://docs.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0
但是,如果您可以使用 fiddle 中的 c# 9,则可以将类型定义为 record
而不是 class
。记录已建立在逐场比较中。参见 https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#characteristics-of-records
因此 public class ResponseExample
将变为 public record ResponseExample
,您的代码将按预期工作。
如果性能不是什么大问题,可以使用Newtonsoft.Json。我们将能够比较不同类型的对象以及 运行 深度相等检查。
首先安装包:
Install-Package Newtonsoft.Json
这是代码片段:
public static bool DeepEqualsUsingJson<T>(IList<T> l1, IList<T> l2)
{
if (ReferenceEquals(l1, l2))
return true;
if (ReferenceEquals(l2, null))
return false;
if (l1.Count != l2.Count)
return false;
var l1JObject = l1.Select(i => JObject.FromObject(i)).ToList();
var l2JObject = l2.Select(i => JObject.FromObject(i)).ToList();
foreach (var o1 in l1JObject)
{
var index = l2JObject.FindIndex(o2 => JToken.DeepEquals(o1, o2));
if (index == -1)
return false;
l2JObject.RemoveAt(index);
}
return l2JObject.Count == 0;
}