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