简体   繁体   中英

LINQ (or something else) to compare a pair of values from two lists (in any order)?

Basically, I have two IEnumerable<FooClass> s where each FooClass instance contains 2 properties: FirstName, LastName.

The instances on each of the enumerables is the same. 相同。 Instead, I need to check against the properties on each of the instances. I'm not sure of the most efficient way to do this, but basically I need to make sure that both lists contain similar data (not the same instance, but the same values on the properties). I don't have access to the FooClass itself to modify it.


I should say that the FooClass is a type of Attribute class, which has access to the Attribute.Match() method, so I don't need to check each properties individually.


Based on the comments, I've updated the question to be more specific and changed it slightly... This is what I have so far:

 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. } 

Enumerable.SequenceEqual will tell you if the two sequences are identical.

If FooClass has an overridden Equals method that compares the FirstName and LastName , then you should be able to write:

bool equal = List1.SequenceEqual(List2);

If FooClass doesn't have an overridden Equals method, then you need to create an 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();
    }
}

and then you write:

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

You can do in this way:

Define a custom 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());
    }
}

Define an extension method to compare lists in any order and having the same number of equal elements:

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());
}

Usage example:

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());
}

Assuming that

  1. The lists both fit in memory and are unsorted
  2. Case doesn't matter
  3. Names don't contain the character "!"
  4. Names do not contain duplicates:

then

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM