Collection Intersection Helper

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
 
A

Arne Vajhøj

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
 
K

kndg

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

Arne Vajhøj

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
 
A

Arne Vajhøj

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
 
A

Arne Vajhøj

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
 
S

Shapper

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

Arne Vajhøj

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top