比较元组,忽略元素的顺序
Comparing Tuples, Ignoring Order of Elements
假设我有对象 Foo 和 Bar;和名为 A、B 和 C 的 3 个元组。
A = (Foo, Bar)
B = (Bar, Foo)
C = (Foo, Bar)
我想知道它们的元素是否相同,不考虑元素的顺序。所以我想要的结果是;
A.HasSameElementsWith(B) -> True
A.HasSameElementsWith(C) -> True
B.HasSameElementsWith(C) -> True
我知道我可以 运行 一个嵌套循环来比较它们的每个元素。大致如下:
foreach (itemA in A)
{
bool flag = false;
foreach (itemB in B)
{
if(itemA == itemB)
{
flag = true;
break;
}
}
if (!flag) return false;
}
return true;
但这似乎效率不高。有没有更方便的方法呢?
注:
我使用的是泛型,所以 Foo
和 Bar
可以是任何类型。但它们彼此将是同一类型。 (即 Foo
的类型与 Bar
的类型相同)
我会稍微解释一下@doctorlove 的评论。
他提到排序。排序后,您可以逐个元素地比较它们。伪代码:
public bool Compare(A, B){
return (A[1] == B[1] & A[2] == B[2]);
}
如果你有两个 2 元组,那么根据你的规则只有两个选项可以让它们相等,你可以将其写成几乎是单行的方法:
public static bool HasSameElementsWith<T>(this (T, T) tuple1, (T, T) tuple2) =>
(Equals(tuple1.Item1, tuple2.Item1) && Equals(tuple1.Item2, tuple2.Item2)) ||
(Equals(tuple1.Item1, tuple2.Item2) && Equals(tuple1.Item2, tuple2.Item1));
如果每个元组可以有两个以上的项目,那么我会开始将它们视为一个集合,然后问题就变成了两个集合是否具有相同的项目。为此,您可以在 Dictionary<T, int>
中对第一个集合中的每个项目进行计数,然后对第二个集合中的项目进行计数。如果两个集合包含相同的项目,则最后所有计数都应为零。 (如果您确定每个集合中的项目都是唯一的,则可以改用 HashSet<T>
。)在代码中:
public static bool HasSameElementsWith<T>(
this IEnumerable<T> collection1, IEnumerable<T> collection2)
{
var counts = new Dictionary<T, int>();
foreach (var item in collection1)
{
counts.TryGetValue(item, out int count);
count++;
counts[item] = count;
}
foreach (var item in collection2)
{
counts.TryGetValue(item, out int count);
if (count == 0)
return false;
count--;
counts[item] = count;
}
return counts.Values.All(c => c == 0);
}
现在您可以在集合版本之上实现 HasSameElementsWith
的元组版本:
public static bool HasSameElementsWith<T>(this (T, T) tuple1, (T, T) tuple2) =>
HasSameElementsWith(tuple1.ToArray(), tuple2.ToArray());
public static T[] ToArray<T>(this (T, T) tuple) => new[] { tuple.Item1, tuple.Item2 };
假设我有对象 Foo 和 Bar;和名为 A、B 和 C 的 3 个元组。
A = (Foo, Bar)
B = (Bar, Foo)
C = (Foo, Bar)
我想知道它们的元素是否相同,不考虑元素的顺序。所以我想要的结果是;
A.HasSameElementsWith(B) -> True
A.HasSameElementsWith(C) -> True
B.HasSameElementsWith(C) -> True
我知道我可以 运行 一个嵌套循环来比较它们的每个元素。大致如下:
foreach (itemA in A)
{
bool flag = false;
foreach (itemB in B)
{
if(itemA == itemB)
{
flag = true;
break;
}
}
if (!flag) return false;
}
return true;
但这似乎效率不高。有没有更方便的方法呢?
注:
我使用的是泛型,所以 Foo
和 Bar
可以是任何类型。但它们彼此将是同一类型。 (即 Foo
的类型与 Bar
的类型相同)
我会稍微解释一下@doctorlove 的评论。 他提到排序。排序后,您可以逐个元素地比较它们。伪代码:
public bool Compare(A, B){
return (A[1] == B[1] & A[2] == B[2]);
}
如果你有两个 2 元组,那么根据你的规则只有两个选项可以让它们相等,你可以将其写成几乎是单行的方法:
public static bool HasSameElementsWith<T>(this (T, T) tuple1, (T, T) tuple2) =>
(Equals(tuple1.Item1, tuple2.Item1) && Equals(tuple1.Item2, tuple2.Item2)) ||
(Equals(tuple1.Item1, tuple2.Item2) && Equals(tuple1.Item2, tuple2.Item1));
如果每个元组可以有两个以上的项目,那么我会开始将它们视为一个集合,然后问题就变成了两个集合是否具有相同的项目。为此,您可以在 Dictionary<T, int>
中对第一个集合中的每个项目进行计数,然后对第二个集合中的项目进行计数。如果两个集合包含相同的项目,则最后所有计数都应为零。 (如果您确定每个集合中的项目都是唯一的,则可以改用 HashSet<T>
。)在代码中:
public static bool HasSameElementsWith<T>(
this IEnumerable<T> collection1, IEnumerable<T> collection2)
{
var counts = new Dictionary<T, int>();
foreach (var item in collection1)
{
counts.TryGetValue(item, out int count);
count++;
counts[item] = count;
}
foreach (var item in collection2)
{
counts.TryGetValue(item, out int count);
if (count == 0)
return false;
count--;
counts[item] = count;
}
return counts.Values.All(c => c == 0);
}
现在您可以在集合版本之上实现 HasSameElementsWith
的元组版本:
public static bool HasSameElementsWith<T>(this (T, T) tuple1, (T, T) tuple2) =>
HasSameElementsWith(tuple1.ToArray(), tuple2.ToArray());
public static T[] ToArray<T>(this (T, T) tuple) => new[] { tuple.Item1, tuple.Item2 };