Collection Intersection Helper

  • Thread starter Thread starter Shapper
  • Start date Start date
S

Shapper

Hello,

I have two lists of an object:

List<ObjectA> As;
List<ObjectB> Bs;

Is it possible to create one or more methods or extensions that would
make possible to get 3 lists:

List 1 - All objects existing in list A but not in B
List 2 - All objects existing in list B but not in A
List 3 - All objects existing in both Lists.

Basically what I am trying to get is:
A intersection B
A intersection Not B
Not A intersection with B

I would like to define the condition that makes the intersection.
Maybe with a lambda expression?
Does this make sense?

Thank You,
Miguel
 
I have two lists of an object:

List<ObjectA> As;
List<ObjectB> Bs;

Is it possible to create one or more methods or extensions that would
make possible to get 3 lists:

List 1 - All objects existing in list A but not in B
List 2 - All objects existing in list B but not in A
List 3 - All objects existing in both Lists.

Basically what I am trying to get is:
A intersection B
A intersection Not B
Not A intersection with B

I would like to define the condition that makes the intersection.
Maybe with a lambda expression?
Does this make sense?

It is easy for:

List<ObjectX> As;
List<ObjectX> Bs;

(standard LINQ has methods for that)

but for lists of different types I don't think it
makes much sense.

Arne
 
Hello,

I have two lists of an object:

List<ObjectA> As;
List<ObjectB> Bs;

Is it possible to create one or more methods or extensions that would
make possible to get 3 lists:

List 1 - All objects existing in list A but not in B
List 2 - All objects existing in list B but not in A
List 3 - All objects existing in both Lists.

Basically what I am trying to get is:
A intersection B
A intersection Not B
Not A intersection with B

I would like to define the condition that makes the intersection.
Maybe with a lambda expression?
Does this make sense?

Thank You,
Miguel

I would look into HashSet<T> instead. It has all your required operation.
 
It is easy for:

List<ObjectX> As;
List<ObjectX> Bs;

(standard LINQ has methods for that)

but for lists of different types I don't think it
makes much sense.

Demo with same type:

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

namespace E
{
public class Program
{
public static void Main(string[] args)
{
List<string> x = new List<string>{ "A", "B", "C", "D" };
List<string> y = new List<string>{ "C", "D", "E", "F" };
foreach(string s in x.Except(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in y.Except(x))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Intersect(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Union(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
Console.ReadKey();
}
}
}

Arne
 
I would look into HashSet<T> instead. It has all your required operation.

Not only does HashSet<T> have them, but it would also be very
fast at doing them.

But it also have slightly different characteristics than List<T>.

Duplicates. Order. Access by index.

Arne
 
It is easy for:

List<ObjectX> As;
List<ObjectX> Bs;

(standard LINQ has methods for that)

but for lists of different types I don't think it
makes much sense.

Demo with same type:

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

namespace E
{
public class Program
{
public static void Main(string[] args)
{
List<string> x = new List<string>{ "A", "B", "C", "D" };
List<string> y = new List<string>{ "C", "D", "E", "F" };
foreach(string s in x.Except(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in y.Except(x))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Intersect(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Union(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
Console.ReadKey();
}
}
}

And:

foreach(string s in x.Concat(y))
{
Console.Write(" " + s);
}
Console.WriteLine();

if so needed.

Arne
 
Demo with same type:
using System;
using System.Collections.Generic;
using System.Linq;
namespace E
{
public class Program
{
public static void Main(string[] args)
{
List<string> x = new List<string>{ "A", "B", "C", "D" };
List<string> y = new List<string>{ "C", "D", "E", "F" };
foreach(string s in x.Except(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in y.Except(x))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Intersect(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Union(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
Console.ReadKey();
}
}
}

And:

             foreach(string s in x.Concat(y))
             {
                 Console.Write(" " + s);
             }
             Console.WriteLine();

if so needed.

Arne

Hello,

Yes, it can be objects with same type. But what do you mean with
x.Except(y)?

I need to define the condition. Something like:

x.Except(x => y.Id == x.Id)

This is the kind of helper I was looking for.
 
On 19-01-2011 19:54, Arne Vajhøj wrote:
On 19-01-2011 18:04, Shapper wrote:
I have two lists of an object:
List<ObjectA> As;
List<ObjectB> Bs;
Is it possible to create one or more methods or extensions that would
make possible to get 3 lists:
List 1 - All objects existing in list A but not in B
List 2 - All objects existing in list B but not in A
List 3 - All objects existing in both Lists.
Basically what I am trying to get is:
A intersection B
A intersection Not B
Not A intersection with B
I would like to define the condition that makes the intersection.
Maybe with a lambda expression?
Does this make sense?
It is easy for:
List<ObjectX> As;
List<ObjectX> Bs;
(standard LINQ has methods for that)
but for lists of different types I don't think it
makes much sense.
Demo with same type:
using System;
using System.Collections.Generic;
using System.Linq;
namespace E
{
public class Program
{
public static void Main(string[] args)
{
List<string> x = new List<string>{ "A", "B", "C", "D" };
List<string> y = new List<string>{ "C", "D", "E", "F" };
foreach(string s in x.Except(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in y.Except(x))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Intersect(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
foreach(string s in x.Union(y))
{
Console.Write(" " + s);
}
Console.WriteLine();
Console.ReadKey();
}
}
}

And:

foreach(string s in x.Concat(y))
{
Console.Write(" " + s);
}
Console.WriteLine();

if so needed.
Yes, it can be objects with same type. But what do you mean with
x.Except(y)?

I need to define the condition. Something like:

x.Except(x => y.Id == x.Id)

This is the kind of helper I was looking for.

This is going to be a little bit long, but ...

Let us look at this code:

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

namespace E
{
public class Person
{
private string name;
private string address;
public Person(string name, string address)
{
this.name = name;
this.address = address;
}
public string Name
{
get { return name; }
}
public string Address
{
get { return address; }
}
public override string ToString()
{
return name + "/" + address;
}
}
public class Program
{
public static void Main(string[] args)
{
List<Person> x = new List<Person>();
x.Add(new Person("A A", "aa aa aa aa"));
x.Add(new Person("B B", "bbb bbb bbb"));
x.Add(new Person("C C", "cccc cccc"));
List<Person> y = new List<Person>();
y.Add(new Person("C C", "cccc cccc"));
y.Add(new Person("D D", "ddddd"));
Console.WriteLine("x:");
foreach(Person p in x)
{
Console.WriteLine(" " + p);
}
Console.WriteLine("y:");
foreach(Person p in y)
{
Console.WriteLine(" " + p);
}
Console.WriteLine("x except y:");
foreach(Person p in x.Except(y))
{
Console.WriteLine(" " + p);
}
Console.ReadKey();
}
}
}

it outputs:

x:
A A/aa aa aa aa
B B/bbb bbb bbb
C C/cccc cccc
y:
C C/cccc cccc
D D/ddddd
x except y:
A A/aa aa aa aa
B B/bbb bbb bbb
C C/cccc cccc

which as first looks surprising. The problem is that .NET
does not have any way of knowing that two persons with the
name "C C" are actually identical.

There are two ways of solving that.

1) Explicit tell it how to compare in the Except method.

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

namespace E
{
public class Person
{
private string name;
private string address;
public Person(string name, string address)
{
this.name = name;
this.address = address;
}
public string Name
{
get { return name; }
}
public string Address
{
get { return address; }
}
public override string ToString()
{
return name + "/" + address;
}
}
public class PersonEqualityComparer : IEqualityComparer<Person>
{

public bool Equals(Person x, Person y)
{
return x.Name == y.Name;
}
public int GetHashCode(Person obj)
{
return obj.Name.GetHashCode();
}
}
public class Program
{
public static void Main(string[] args)
{
List<Person> x = new List<Person>();
x.Add(new Person("A A", "aa aa aa aa"));
x.Add(new Person("B B", "bbb bbb bbb"));
x.Add(new Person("C C", "cccc cccc"));
List<Person> y = new List<Person>();
y.Add(new Person("C C", "cccc cccc"));
y.Add(new Person("D D", "ddddd"));
Console.WriteLine("x:");
foreach(Person p in x)
{
Console.WriteLine(" " + p);
}
Console.WriteLine("y:");
foreach(Person p in y)
{
Console.WriteLine(" " + p);
}
Console.WriteLine("x except y:");
foreach(Person p in x.Except(y, new PersonEqualityComparer()))
{
Console.WriteLine(" " + p);
}
Console.ReadKey();
}
}
}

2) Embed in the Person class itself how to compare.

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

namespace E
{
public class Person
{
private string name;
private string address;
public Person(string name, string address)
{
this.name = name;
this.address = address;
}
public string Name
{
get { return name; }
}
public string Address
{
get { return address; }
}
public override string ToString()
{
return name + "/" + address;
}
public override bool Equals(object obj)
{
return obj.GetType() == typeof(Person) &&
((Person)obj).Name == name;
}
public override int GetHashCode()
{
return name.GetHashCode();
}
}
public class Program
{
public static void Main(string[] args)
{
List<Person> x = new List<Person>();
x.Add(new Person("A A", "aa aa aa aa"));
x.Add(new Person("B B", "bbb bbb bbb"));
x.Add(new Person("C C", "cccc cccc"));
List<Person> y = new List<Person>();
y.Add(new Person("C C", "cccc cccc"));
y.Add(new Person("D D", "ddddd"));
Console.WriteLine("x:");
foreach(Person p in x)
{
Console.WriteLine(" " + p);
}
Console.WriteLine("y:");
foreach(Person p in y)
{
Console.WriteLine(" " + p);
}
Console.WriteLine("x except y:");
foreach(Person p in x.Except(y))
{
Console.WriteLine(" " + p);
}
Console.ReadKey();
}
}
}

If the desired way to compare in Except is a natural way to compare,
then I will recommend the second way, because implementing those
two methods will be useful in a lot of other contexts.

In your specific case then comparing on Id sounds as a natural
comparison.

Arne
 
Back
Top