C# .NET检查是否有两个IEnumerable<;T>;有相同的元素

C# .NET检查是否有两个IEnumerable<;T>;有相同的元素,c#,.net,linq,list,set,C#,.net,Linq,List,Set,可能重复: 我需要验证两个IEnumerable列表是否具有相同的元素,但顺序不一定相同 我的目标是.NET3.5 以下是测试。问题是,应该如何实现HasSameElements() var l1 = new[]{1,2,3}; var l2 = new[]{3,1,2}; bool rez1 = l1.HasSameElements(l2);//should be true var l3 = new[]{1,2,3,2}; var l4 = new[]{3,1,2,2}; bool re

可能重复:

我需要验证两个
IEnumerable
列表是否具有相同的元素,但顺序不一定相同

我的目标是.NET3.5

以下是测试。问题是,应该如何实现
HasSameElements()

var l1 = new[]{1,2,3};
var l2 = new[]{3,1,2};

bool rez1 = l1.HasSameElements(l2);//should be true

var l3 = new[]{1,2,3,2};
var l4 = new[]{3,1,2,2};
bool rez2 = l3.HasSameElements(l4);//should be true

var l5 = new[]{1,2,3,2};
var l6 = new[]{1,2,3};
bool rez3 = l5.HasSameElements(l6);//should be false
传统注释:

  • 在示例中,我使用的是IEnumerable,但T可以是任何东西。是否不一定要实现
    i可比较

  • Enumerable.SequenceEquals()本身不起作用,它要求元素的顺序相同

  • 下面是一个用于
    HasElements
    的模板:

[只是一些占位符文本作为标记“代码格式”错误的解决方法]

public static class Extensions {
    public static bool HasElements(this IEnumerable<T> l1, IEnumerable<T> l2){
        throw new NotImplementedException();
    } 
}
公共静态类扩展{
公共静态布尔元素(此IEnumerable l1、IEnumerable l2){
抛出新的NotImplementedException();
} 
}
使用:

您还可以传递自定义比较器

public static class Extensions
{
    public static bool HasElements(
        this IEnumerable<T> l1, 
        IEnumerable<T> l2,
        IEqualityComparer<T> comparaer)
    {
        l2.Count() == l1.Count() && 
        return l1.Intersect(l2, comparer).Count() == l2.Count();
    } 
}
公共静态类扩展
{
公共静态布尔元素(
这是可数l1,
IEnumerable l2,
IEqualityComparer(比较者)
{
l2.Count()==l1.Count()&&
返回l1.Intersect(l2,比较器).Count()==l2.Count();
} 
}

尽管Cristi的基于
的方法比基于
的方法要好得多,但您可能会侥幸逃脱:

source.Sort().SequenceEqual(target.Sort());

如果是单元测试,我不会担心性能。当然,您需要确保您的排序是稳定的。

我会这样做

 public static bool HasSameElements<T>(this IEnumerable<T> source, IEnumerable<T> target)
 {
     return (source.Count() == target.Count() && source.All(a => target.Contains(a)));
 }
publicstaticboolhasamelements(此IEnumerable源,IEnumerable目标)
{
返回(source.Count()==target.Count()&&source.All(a=>target.Contains(a));
}

因为输入可以包含重复项,您只能使用。一种算法是:

if the lists contain a different number of elements return false

make a copy of listA
foreach element in listB 
  if the element exists in copyA remove the first match from copyA
  otherwise return false

对于实现,您可以查看in-FluentAssert的逻辑。

只需构建一个字典,将每个对象映射到它在序列中出现的次数,然后检查生成的字典是否相等

在这里:

静态类EnumerableExtensions{
公共静态bool hasamelementsas(
这是第一次,
数秒
) {
var firstMap=first
.GroupBy(x=>x)
.ToDictionary(x=>x.Key,x=>x.Count());
var secondMap=second
.GroupBy(x=>x)
.ToDictionary(x=>x.Key,x=>x.Count());
返回
firstMap.Keys.All(x=>
secondMap.Keys.Contains(x)&&firstMap[x]==secondMap[x]
) &&
secondMap.Keys.All(x=>
firstMap.Keys.Contains(x)&&secondMap[x]==firstMap[x]
);
}
}

显然,重复的代码可以重构为助手方法,但这只会混淆这里的想法。对于
GroupBy
操作,您可以选择接受
IEqualityComparer
。另外,您应该通过添加
null
guards和其他什么来生成代码。

取决于哪个是较大的集合…将不起作用。可枚举。Intersect返回一个集合。new[]{2,2}.Intersect(new[]{2,2})返回{2}刚开始只是检查计数是否等于,这很容易。如果可以的话,你真的想避免遍历序列两次。@Mitch:把这个注释作为一个答案,我会接受的。问题是重复的,但更好的答案是:,如果包含的对象是可排序的…这平均是
O(n log n)
。我认为我的方法()是
O(n)
。如果可以的话,你真的希望避免遍历序列两次。GroupBy()有O(n)复杂度吗?@Cristi Diaconescu:是的,应该是
O(n)
if the lists contain a different number of elements return false

make a copy of listA
foreach element in listB 
  if the element exists in copyA remove the first match from copyA
  otherwise return false
static class EnumerableExtensions {
    public static bool HasSameElementsAs<T>(
        this IEnumerable<T> first,
        IEnumerable<T> second
    ) {
        var firstMap = first
            .GroupBy(x => x)
            .ToDictionary(x => x.Key, x => x.Count());
        var secondMap = second
            .GroupBy(x => x)
            .ToDictionary(x => x.Key, x => x.Count());
        return 
            firstMap.Keys.All(x =>
                secondMap.Keys.Contains(x) && firstMap[x] == secondMap[x]
            ) &&
            secondMap.Keys.All(x =>
                firstMap.Keys.Contains(x) && secondMap[x] == firstMap[x]
            );
    }
}