LINQ(或其他)比较两个列表中的一对值(以任何顺序)?

时间:2011-10-07 21:39:01

标签: c# performance linq comparison ienumerable

基本上,我有两个IEnumerable<FooClass>,其中每个FooClass实例包含2个属性:FirstName,LastName。

每个可枚举的实例 NOT 相同。相反,我需要检查每个实例上的属性。我不确定最有效的方法,但基本上我需要确保两个列表包含相似的数据(不是相同的实例,但属性上的值相同)。我无法访问FooClass本身来修改它。


我应该说FooClass是一种Attribute类,可以访问Attribute.Match()方法,所以我不需要单独检查每个属性。

<小时/> 根据评论,我更新了问题更具体,稍微改了一下......这就是我到目前为止所做的:

public void Foo()
{
        var info = typeof(MyClass);
        var attributes = info.GetCustomAttributes(typeof(FooAttribute), false) as IEnumerable<FooAttribute>;

        var validateAttributeList = new Collection<FooAttribute>
            {
                new FooAttribute(typeof(int), typeof(double));
                new FooAttribute(typeof(int), typeof(single));
            };

        //Make sure that the each item in validateAttributeList is contained in
        //the attributes list (additional items in the attributes list don't matter).
        //I know I can use the Attribute.Match(obj) to compare.
}

3 个答案:

答案 0 :(得分:3)

Enumerable.SequenceEqual会告诉您两个序列是否相同。

如果FooClass有一个被覆盖的Equals方法,可以比较FirstNameLastName,那么您应该能够写一下:

bool equal = List1.SequenceEqual(List2);

如果FooClass没有覆盖Equals方法,则需要创建IEqualityComparer<FooClass>

class FooComparer: IEqualityComparer<FooClass>
{
    public bool Equals(FooClass f1, FooClass f2)
    {
        return (f1.FirstName == f2.FirstName) && (f1.LastName == f2.LastName);
    }
    public int GetHashCode()
    {
        return FirstName.GetHashCode() ^ LastName.GetHashCode();
    }
}

然后你写:

var comparer = new FooComparer();
bool identical = List1.SequenceEqual(List2, comparer);

答案 1 :(得分:2)

你可以这样做:

定义自定义 IEqualityComparer<FooAttribute>

class FooAttributeComparer : IEqualityComparer<FooAttribute>
{
    public bool Equals(FooAttribute x, FooAttribute y)
    {
        return x.Match(y);
    }
    public int GetHashCode(FooAttribute obj)
    {
        return 0;
        // This makes lookups complexity O(n) but it could be reasonable for small lists 
        // or if you're not sure about GetHashCode() implementation to do.
        // If you want more speed you could return e.g. :
        // return obj.Field1.GetHashCode() ^ (17 * obj.Field2.GetHashCode());
    }
}

定义一种扩展方法,以任意顺序比较列表并具有相同数量的相等元素:

public static bool ListContentIsEqualInAnyOrder<T>(
this IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer)
{
    var lookup1 = list1.ToLookup(x => x, comparer);
    var lookup2 = list2.ToLookup(x => x, comparer);
    if (lookup1.Count != lookup2.Count)
        return false;
    return lookup1.All(el1 => lookup2.Contains(el1.Key) && 
            lookup2[el1.Key].Count() == el1.Count());
}

用法示例:

static void Main(string[] args)
{
    List<FooAttribute> attrs = new List<FooAttribute>
    {
        new FooAttribute(typeof(int), typeof(double)),
        new FooAttribute(typeof(int), typeof(double)),
        new FooAttribute(typeof(bool), typeof(float)),
        new FooAttribute(typeof(uint), typeof(string)),
    };
    List<FooAttribute> attrs2 = new List<FooAttribute>
    {
        new FooAttribute(typeof(uint), typeof(string)),
        new FooAttribute(typeof(int), typeof(double)),
        new FooAttribute(typeof(int), typeof(double)),
        new FooAttribute(typeof(bool), typeof(float)),
    };

    // this returns true
    var listEqual1 = attrs.ListContentIsEqualInAnyOrder(attrs2, new FooAttributeComparer());

    // this returns false
    attrs2.RemoveAt(1);
    var listEqual2 = attrs.ListContentIsEqualInAnyOrder(attrs2, new FooAttributeComparer());
}

答案 2 :(得分:0)

假设

  1. 列表既适合内存又未分类
  2. 案件无关紧要
  3. 名称不包含字符“!”
  4. 名称不包含重复项:
  5. 然后

    var setA = new HashSet<String>(
        firstEnumerable.Select(i => i.FirstName.ToUpper() + "!" + i.LastName.ToUpper()));
    var setB = new HashSet<String>(
        secondEnumerable.Select(i => i.FirstName.ToUpper() + "!" + i.LastName.ToUpper()));
    return setA.SetEquals(setB);