Open atifaziz opened 9 years ago
This could be improved with something like the HashSet implementation of ExceptBy, if HashSet didn't require an IEqualityComparer. Is there anything like Hashset that takes an Func<T, T, bool> as a comparer parameter?
public static class HashSetDelegate
{
public static HashSet<T> Create<T>(Func<T, T, bool> func)
{
return new HashSet<T>(new FuncIEqualityComparerAdapter<T>(func));
}
private class FuncIEqualityComparerAdapter<U> : IEqualityComparer<U>
{
private Func<U, U, bool> func;
public FuncIEqualityComparerAdapter(Func<U, U, bool> func)
{
this.func = func;
}
public bool Equals(U a, U b)
{
return func(a, b);
}
public int GetHashCode(U obj)
{
return 0;
}
}
}
public class HashSetTest
{
public static void Main()
{
HashSet<string> s = HashSetDelegate.Create((string a, string b) => string.Compare(a, b, true) == 0);
}
}
Here is a pretty simple implementation I came up with for these methods. I've left out argument validation to keep code samples brief. I also ended each method name in "Delegate" to distinguish them from the similar methods in PR #169, but I do not think these are great final names.
public static IEnumerable<TSource1> IntersectByDelegate<TSource1, TSource2>(
this IEnumerable<TSource1> first,
IEnumerable<TSource2> second,
Func<TSource1, TSource2, bool> comparer) {
var keys = second.ToArray();
return first.Where(f => keys.Any(s => comparer(f, s)));
}
public static IEnumerable<TSource1> ExceptByDelegate<TSource1, TSource2>(
this IEnumerable<TSource1> first,
IEnumerable<TSource2> second,
Func<TSource1, TSource2, bool> comparer) {
var keys = second.ToArray();
return first.Where(f => keys.All(s => comparer(f, s)));
}
One issue I see is that, without adding more delegate parameters, there isn't any "distinctness" being enforced. This would require a more complicated implementation, which may also require another version of DistinctBy
as part of it.
public static IEnumerable<TSource1> ExceptByDelegate<TSource1, TSource2>(
this IEnumerable<TSource1> first,
IEnumerable<TSource2> second,
Func<TSource1, TSource2, bool> comparer,
Func<TSource1, TSource1, bool> distinctnessComparer1,
Func<TSource2, TSource2, bool> distinctnessComparer2) {
var keys = second
.DistinctBy(distinctnessComparer2)
.ToArray();
return first
.Where(f => keys.All(s => comparer(f, s)))
.DistinctBy(distinctnessComparer1);
}
public static IEnumerable<TSource> DistinctByDelegate(
this IEnumerable<TSource> sequence,
Func<TSource, TSource, bool> comparer) {
var queue = new Queue<TSource>(sequence);
while (queue.Any()) {
var item = queue.Pop();
if (queue.All(x => !comparer(item, x)))
yield return item;
}
}
For most use cases, the methods in PR #169 can accomplish what ExceptByDelegate
and IntersectByDelegate
methods are for, but there are a few benefits here.
IEqualityComparer<T>
(which is not affected by requiring distinctness). (x, y) => (x < y)
. These "non-equals" comparisons are completely unrepresentable with the overloads in PR #169, but I imagine they make up a very small percentage of use cases.So, here are a few opinion questions:
ExceptByDelegate
and IntersectByDelegate
provide enough value to add to the API?DistinctByDelegate
be added to the public API? Should that be a separate issue?
Originally reported on Google Code with ID 80
Reported by
agrath
on 2013-07-09 22:34:29