-1

I'm trying to find an intersect with LINQ.

I have this kind of object:

 public class objA
 {
     public int foo1{ get; set; }
     public string foo2 { get; set; }
     public bool foo3 { get; set; }
 }

Sample:

List<objA> name1= new List<int>() {{1,"bb",true} , {2,"cc",false}};
List<objA> name2= new List<int>();
List<objA> name3= new List<int>() { {1,"bb",true} };
List<objA> name4= new List<int>() { {1,"bb",true} , {2,"cc",false} };
List<objA> name5= new List<int>() { {1,"bb",true} };

Want to return: {1,"bb",true} as it exists in all lists.. If I run:

List<objA>= name1
            .Intersect(name2)
            .Intersect(name3)
            .Intersect(name4)
            .Intersect(name5).ToList();

It returns nothing as list 2 is empty.

I am aware of this message: LINQ intersect, multiple lists, some empty

But I dont know how to do with objects.

Community
  • 1
  • 1
Yogi_Bear
  • 512
  • 2
  • 10
  • 24

5 Answers5

3

To start with lets define an extension method that will perform an intersection, but ignoring any list that is empty.

public static class Ex
{
    public static IEnumerable<T> IntersectIgnoreEmpty<T>(this IEnumerable<T> @this, IEnumerable<T> other, IEqualityComparer<T> comparer)
    {
        return other.Any() ? @this.Intersect(other, comparer) : @this;
    }
}

Now we define an equality comparer for objA:

public class objAEqualityComparer : IEqualityComparer<objA>
{
    public bool Equals(objA left, objA right)
    {
        return
            left.foo1 == right.foo1
            && left.foo2 == right.foo2
            && left.foo3 == right.foo3;
    }

    public int GetHashCode(objA @this)
    {
        return
            @this.foo1.GetHashCode()
            ^ @this.foo2.GetHashCode()
            ^ @this.foo3.GetHashCode();
    }
}

Then we can run this code:

List<objA> name1 = new List<objA>()
{
    new objA { foo1 = 1, foo2 = "bb", foo3 = true },
    new objA { foo1 = 2, foo2 = "cc", foo3 = false },
};
List<objA> name2 = new List<objA>();
List<objA> name3 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };
List<objA> name4 = new List<objA>()
{
    new objA { foo1 = 1, foo2 = "bb", foo3 = true },
    new objA { foo1 = 2, foo2 = "cc", foo3 = false },
};
List<objA> name5 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };

objAEqualityComparer comparer = new objAEqualityComparer();

List<objA> result =
    name1
        .IntersectIgnoreEmpty(name2, comparer)
        .IntersectIgnoreEmpty(name3, comparer)
        .IntersectIgnoreEmpty(name4, comparer)
        .IntersectIgnoreEmpty(name5, comparer)
        .ToList();

The result we get is:

result

You could always override Equals and GetHashCode in objA, but that would require making objA read-only so as to not break the equality contract - making an IEqualityComparer<objA> is a better choice in this case as it allows objA to be read/write.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

First, name2 is empty. "Intersect" always gives nothing. So remove name2.

List<objA> output = name1
    .Intersect(name3)
    .Intersect(name4)
    .Intersect(name5).ToList();

Second, removing ".Intersect(name2)" still gives nothing.

It is becuase you have to implement both "Equal" and "GetHashCode" method in custom class.

public class objA
{
    public int foo1{ get; set; }
    public string foo2 { get; set; }
    public bool foo3 { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is objA == false) return false;

        var a = (objA)obj;
        return a.foo1 == foo1
            && a.foo2 == foo2
            && a.foo3 == foo3;
    }
    public override int GetHashCode()
    {
        return foo1.GetHashCode()
            ^ foo2.GetHashCode()
            ^ foo3.GetHashCode();
    }
}

It is because "Intersect" uses "Equal" to compare objects. And it uses "GetHashCode" to speed up.

Tommy
  • 3,044
  • 1
  • 14
  • 13
  • 1
    Removing `name2` is probably not an option in actually code as how would you know it is empty or not? – Enigmativity Jun 21 '16 at 07:38
  • Removing name2 is in response to "It returns nothing as list 2 is empty". Of course if empty name2 causing empty result is accepted name2 can be kept. – Tommy Jun 21 '16 at 07:40
0

If you want to understand precisely why your code isn't working then Tommy's answer is perfect - remove the ".Intersect(name2)" line (because "name2" doesn't have any items and so intersecting with it will always return in zero results) and then implement GetHashCode and Equals methods on your "objA" class.

However, if you just want to make your code work in the simplest way, you can change objA from a class to a struct since structs automatically receive GetHashCode and Equals implementations that do what you want.

I've fixed up your original code that so that it compiles and changed "objA" into a struct and now the final "x" value will be a list with a single value (foo1 = 1, foo2 = "bb", foo3 = true).

using System.Collections.Generic;
using System.Linq;

namespace StackOverflowAnswer
{
    public struct objA
    {
        public int foo1 { get; set; }
        public string foo2 { get; set; }
        public bool foo3 { get; set; }
    }

    class Program
    {
        static void Main()
        {
            List<objA> name1 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name2 = new List<objA>();
            List<objA> name3 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };
            List<objA> name4 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name5 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };

            List<objA> x = name1
                //.Intersect(name2)
                .Intersect(name3)
                .Intersect(name4)
                .Intersect(name5)
                .ToList();
        }
    }
}

Having read the comments on other answers for this question, maybe I've missed part of the point - is the desire that empty sets should effectively be ignored when performing an "intersect"?

If so, then you could do this by writing your own version of "Intersect", which ignores empty sets - eg.

using System.Collections.Generic;
using System.Linq;

namespace StackOverflowAnswer
{
    public struct objA
    {
        public int foo1 { get; set; }
        public string foo2 { get; set; }
        public bool foo3 { get; set; }
    }

    class Program
    {
        static void Main()
        {
            List<objA> name1 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name2 = new List<objA>();
            List<objA> name3 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };
            List<objA> name4 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true }, new objA { foo1 = 2, foo2 = "cc", foo3 = false } };
            List<objA> name5 = new List<objA>() { new objA { foo1 = 1, foo2 = "bb", foo3 = true } };

            List<objA> x = name1
                .IntersectUnlessEmpty(name2)
                .IntersectUnlessEmpty(name3)
                .IntersectUnlessEmpty(name4)
                .IntersectUnlessEmpty(name5)
                .ToList();
        }
    }

    public static class IEnumerableExtensions
    {
        public static IEnumerable<T> IntersectUnlessEmpty<T>(this IEnumerable<T> source, IEnumerable<T> second)
        {
            return second.Any() ? source.Intersect(second) : source;
        }
    }
}

The final "x" value in this case is still a list with a single item - the empty "name2" data did not prevent the non-empty lists from being intersected.

Dan Roberts
  • 2,244
  • 16
  • 31
-1

The same way:

public IEnumerable<T> IntersectsNonEmpty<T>(
                          params IEnumerable<T>[] input)
{
   var nonEmpty = input.Where(i => i.Any()).ToList();
   if (!nonEmpty.Any()) return false; //or whatever
   return nonEmpty.Aggregate((l1, l2) => l1.Intersect(l2));
}

Usage:

List<objA> result =  IntersectsNonEmpty(name1, name2, name3, name4 .. and go on)

Be aware that objA should have Equalsoverriden in order to consider different instances with the same fields values as the same:

public class objA
{
    public override bool Equals(object other)
    {
          // impl
    }
}
pwas
  • 3,225
  • 18
  • 40
  • This code doesn't compile. You also must override `GetHashCode` whenever you override `Equals`. – Enigmativity Jun 21 '16 at 07:36
  • You've also copied the code from someone else's answer: http://stackoverflow.com/a/3191957/259769 – Enigmativity Jun 21 '16 at 07:42
  • @Enigmativity please read answer carefully. The OP points out the original answer and I wrote 'The same way" - what should be quite obvious that I reffer the answer in the link. – pwas Jun 21 '16 at 07:50
  • But you edited the code and made it not compile. This answer doesn't work. Had you provided a working answer it would be fine, but to break it like that is just bad form. – Enigmativity Jun 21 '16 at 07:54
-1

The reason of empty result is name2 list empty.If you add {1,"bb",true} in the name2,then result will not be empty.

Mehmet
  • 739
  • 1
  • 6
  • 17