IEnumerable<T> feature suggest

Mar 2, 2011 at 2:33 AM

In some cases, we need to do some set manipulation like Intersect, Union, Except, etc., but the intrinsic .NET framework provides only IEqualityComparer<T> as the comparer parameter in these methods which made it is very inconvenient to use: You have to create a new class which implements IEqualityComparer<T>!!!

If it could take a lambda expression as a key extractor for comparing, that would be very nice!

ex: set1.Except(set2, s2 => s2.KeyField);

Take a look at http://blog.lavablast.com/post/2010/05/05/Lambda-IEqualityComparer3cT3e.aspx and https://gist.github.com/391397, there is the implementation!

Developer
Mar 2, 2011 at 3:36 AM

Could you not use LinQ?

With LinQ you could do the following

[TestMethod]
public void IEnumberableTest()
{
	var strings = new List<string> { "value1", "value2", "value3" };
	var strings2 = new List<string> { "string1", "string2", "value3" };
	var stringsUnionResult = new List<string> { "value1", "value2", "value3", "string1", "string2" };
	var stringsExceptResult = new List<string> { "value1", "value2" };

	var stringUnion = strings.Union(strings2).ToList();
	var stringExcept = strings.Except(strings2).ToList();

	Assert.AreEqual(stringUnion, stringsUnionResult);
	Assert.AreEqual(stringExcept, stringsExceptResult);
}

note: List is of IEnumerable type

This is what you are referring to?

Mar 2, 2011 at 7:11 AM

No, that's not what I'm referring to.

Just like etienneT says:

"You could do that for simple scenario.  But the code enables you to do more than this.  For example if you would want to have all the distinct customers on a collection named 'list'.  You can't really use list.Distinct() because it'll be distinct based on the instance and not on the key.  By using one of the new extension method I provide in the code sample, you could do this: 
list.Distinct(y => y.CustomerID); 

This is telling the Distinct method how to compare each objects and only select the distinct Customers with a unique CustomerID. 

You can use the same principle with Except, Intersect, SequenceEqual, Union."

For example: say there is a class DataItem defined below

public class DataItem
{
  public string Filename { get; set; }
  public string Checksum { get; set; }
}

List<DataItem> set1 = service.GetSomeItems();
List<DataItem> set2 = service.GetSomeOtherItems();

// find the items in set1 but not is set2 by ONLY Filename without concerning the Checksum.
var result1 = set1.Except(set2, s => s.Filename); // with the extension methods

Without the extension methods, the task above will be more tricky!

Developer
Mar 2, 2011 at 5:24 PM

The KeyEqualityComparer in those links are a bit too specific to the authors needs.  The general purpose class is much simplier.

Given this class (and it's non-generic helper)

public 	class EqualityComparer<T, U> : IEqualityComparer<T>
	where U: IEquatable<U>
{
	Func<T, U> Selector; 
	public EqualityComparer(Func<T,U> selector)
	{
		Selector = selector;
	}
	#region IEqualityComparer<T> Members
 
	public bool Equals(T x, T y)
	{
		return Selector(x).Equals(Selector(y));
	}
 
	public int GetHashCode(T obj)
	{
		return Selector(obj).GetHashCode();
	}
 
	#endregion
}
public static class EqualityComparer
{
	public static EqualityComparer<T, U> Build<T, U>(Func<T, U> selector)
			where U : IEquatable<U>
	{
		return new EqualityComparer<T, U>(selector);
	}
 
}
Then this : 

 List<Exception> excepts1 = new List<Exception>();
List<Exception> excepts2 = new List<Exception>();
var result1 = set1.Except(set2, new s => s.Message);

 

can be written as this:

 

	List<Exception> excepts1 = new List<Exception>();
	List<Exception> excepts2 = new List<Exception>();
 
	var x = excepts1.Except(excepts2, EqualityComparer.Build( (Exception exc) => exc.Message));
Adding extension method overloads to simplify the call should be trivial.