David Archer
David Archer

Reputation: 2028

C# Linq intersect/except with one part of object

I've got a class:

class ThisClass
{
  private string a {get; set;}
  private string b {get; set;}
}

I would like to use the Intersect and Except methods of Linq, i.e.:

private List<ThisClass> foo = new List<ThisClass>();
private List<ThisClass> bar = new List<ThisClass>();

Then I fill the two lists separately. I'd like to do, for example (and I know this isn't right, just pseudo code), the following:

foo[a].Intersect(bar[a]);

How would I do this?

Upvotes: 27

Views: 38839

Answers (8)

You can try the following. This is a lambda expression in which we define the item which is a ThisClass variable and the equality as an internal lambda expression between foo and bar. In where clause we get the elements that satisfy the equality creteria depending on field a, and because this return an anonymous type of class we select the q.item which represents all the items of ThisClass that satisfy the equality and gives type of IEnumerable.

IEnumerable<ThisClass> Intersection = 
foo.Select(x => new 
{
  item = x, 
  equality = bar.Any(y => y.a.Equals(x.a))
}).Where(w => w.equality).Select(q => q.item);

Upvotes: -1

tyr
tyr

Reputation: 21

I know this is old but couldn't you also just override the Equals & GetHashCode on the class itself?

class ThisClass
{
  public string a {get; set;}
  private string b {get; set;}

  public override bool Equals(object obj)
  {
    // If you only want to compare on a
    ThisClass that = (ThisClass)obj;
    return string.Equals(a, that.a/* optional: not case sensitive? */);
  }

  public override int GetHashCode()
  {
    return a.GetHashCode();
  }
}

Upvotes: 0

Zach Johnson
Zach Johnson

Reputation: 810

Not sure of the speed of this compared to intersect and compare but how about:

//Intersect
var inter = foo.Where(f => bar.Any(b => b.a == f.a));
//Except - values of foo not in bar
var except = foo.Where(f => !bar.Any(b => b.a == f.a));

Upvotes: 7

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236308

Maybe

// returns list of intersecting property 'a' values
foo.Select(f => f.a).Intersect(bar.Select(b => b.a));

BTW property a should be public.

Upvotes: 39

Patryk Ćwiek
Patryk Ćwiek

Reputation: 14328

If you want a list of a single property you'd like to intersect then all the other pretty LINQ solutions work just fine. BUT! If you'd like to intersect on a whole class though and as a result have a List<ThisClass> instead of List<string> you'll have to write your own equality comparer.

foo.Intersect(bar, new YourEqualityComparer());

same with Except.

public class YourEqualityComparer: IEqualityComparer<ThisClass>
{

    #region IEqualityComparer<ThisClass> Members


    public bool Equals(ThisClass x, ThisClass y)
    {
        //no null check here, you might want to do that, or correct that to compare just one part of your object
        return x.a == y.a && x.b == y.b;
    }


    public int GetHashCode(ThisClass obj)
    {
        unchecked
        {
            var hash = 17;
                            //same here, if you only want to get a hashcode on a, remove the line with b
            hash = hash * 23 + obj.a.GetHashCode();
            hash = hash * 23 + obj.b.GetHashCode();

            return hash;    
        }
    }

    #endregion
}

Upvotes: 41

Avner Shahar-Kashtan
Avner Shahar-Kashtan

Reputation: 14700

What exactly is the desired effect? Do you want to get a list of strings composed of all the a's in your classes, or a list of ThisClass, when two ThisClass instances are identified via unique values of a?

If it's the former, the two answers from @lazyberezovksy and @Tilak should work. If it's the latter, you'll have to override IEqualityComparer<ThisClass> or IEquatable<ThisClass> so that Intersect knows what makes two instances of ThisClass equivalent:

 private class ThisClass : IEquatable<ThisClass>
 {
     private string a;

     public bool Equals(ThisClass other)
     {
        return string.Equals(this.a, other.a);
     }
 }

then you can just call:

 var intersection = foo.Intersect(bar);     

Upvotes: -1

Pongsathon.keng
Pongsathon.keng

Reputation: 1435

You should create IEqualityComparer. You can pass the IEqualityComparer to Intersect() method. This will help you get List(which intersect with bar) easier.

var intersectionList = foo.Intersect(bar, new ThisClassEqualityComparer()).ToList();


class ThisClassEqualityComparer : IEqualityComparer<ThisClass>
{

    public bool Equals(ThisClass b1, ThisClass b2)
    {
        return b1.a == b2.a;
    }


    public int GetHashCode(Box bx)
    {
       // To ignore to compare hashcode, please consider this.
       // I would like to force Equals() to be called
       return 0;
    }

}

Upvotes: -3

Tilak
Tilak

Reputation: 30728

foo.Select(x=>x.a).Intersect(bar.Select(x=>x.a))

Upvotes: 1

Related Questions