So I've looked through about 20 examples on this on SO and elsewhere, but haven't found one which covers what I'm trying to do. This - Can I specify my explicit type comparator inline? - looks like what I need, but doesn't go far enough (or I don't understand how to take it further).
- I have a List of LoadData, the LoadData object has fields of both reference and value types
- Need to group on a mixture of ref and value fields, project the output to an anonymous type
Need (I think) to provide a custom IEqualityComparer to specify how to compare the GroupBy fields, but they are an anonymous type
private class LoadData { public PeriodEndDto PeriodEnd { get; set; } public ComponentDto Component { get; set; } public string GroupCode { get; set; } public string PortfolioCode { get; set; } }
The best GroupBy query I have going so far:
var distinctLoads = list.GroupBy(
dl => new { PeriodEnd = dl.PeriodEnd,
Component = dl.Component,
GroupCode = dl.GroupCode },
(key, data) => new {PeriodEnd = key.PeriodEnd,
Component = key.Component,
GroupCode = key.GroupCode,
PortfolioList = data.Select(d=>d.PortfolioCode)
.Aggregate((g1, g2) => g1 + "," + g2)},
null);
This groups, but there are still duplicates.
- How can I specify custom code to compare the GroupBy fields? For example, the Components could be compared by Component.Code.
The problem here is that your key type is anonymous, which means you can't declare a class that implements
IEqualityComparer<T>
for that key type. While it would be possible to write a comparator which compared anonymous types for equality in a custom manner (via a generic method, delegates and type inference), it wouldn't be terribly pleasant.The two simplest options are probably:
PeriodEndDto
andComponentDto
. If there's a natural equality you'd want to use everywhere, this is probably the sanest option. I'd recommend implementingIEquatable<T>
as wellGetHashCode
andEquals
on that, or you could write a custom equality comparer in the normal way.EDIT:
ProjectionEqualityComparer
wouldn't really work. It would be feasible to write something similar though - a sort ofCompositeEqualityComparer
which allowed you create an equality comparer from several "projection + comparer" pairs. It would be pretty ugly compared with the anonymous type though.EDIT:
As Jon Skeet points out, this solution seems better than it is, if you don't think too hard about it, because I have forgotten to implement GetHashCode. Having to implement GetHashCode makes this approach, as Jon says in his answer, "not terribly pleasant." Presumably, this is also the explanation for the (so-called "inexplicable") absence of
EqualityComparer<T>.Create()
in the framework. I'll leave the answer for reference, as examples of what not to do, can be instructive as well.ORIGINAL ANSWER:
You could use the approach suggested by the
Comparer<T>.Create
pattern introduced in .NET 4.5 (but inexplicably absent inEqualityComparer<T>
). To do so, create aDelegateEqualityComparer<T>
class:Then write wrappers around the GroupBy methods to accept a
Func<TKey, TKey, bool>
delegate in place of theIEqualityComparer<TKey>
parameter. These methods wrap the delegate in aDelegateEqualityComparer<T>
instance, and pass that on to the corresponding GroupBy method. Example:Finally, at your call site, you would use something like this expression for the
equalityComparison
argument: