Is there already a function in C# that can perform a "Conditional Zip"?
I.e.
Is there a function that allows different length inputs and takes a predicate which determines when to increment the smaller sources enumerator, such that all elements in the larger source are seen?
As a contrived example, lets assume we have an enumerable of prime numbers and an enumerable of integers (both sorted ascending). We want to produce a new enumerable that holds the prime and all integers since the previous prime.
{2, 3, 5, 7, 11}
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10,}
{2, [1]}, {3,[]}, {5, [4]}, {7, [6]}, {11, [8,9,10]}
My solution:
public static IEnumerable<Tuple<T1, IEnumerable<T2>>> ConditionalZip<T1, T2>(
this IEnumerable<T1> src1,
IEnumerable<T2> src2,
Func<T1, T2, bool> check)
{
var list = new List<T2>();
using(var enumerator = src2.GetEnumerator())
{
foreach(var item1 in src1)
{
while(enumerator.MoveNext())
{
var pickedItem = enumerator.Current;
if(check(item1, pickedItem))
{
list.Add(pickedItem);
}
else
{
break;
}
}
var items = list.ToArray();
list.Clear();
yield return new Tuple<T1, IEnumerable<T2>>(item1, items);
}
}
}
It guarantees that both enumerations will be enumerated only once.
Usage:
var src1 = new int[] { 2, 3, 5, 7, 11 };
var src2 = Enumerable.Range(1, 11);
Func<int, int, bool> predicate = (i1, i2) => i1 > i2;
var result = src1.ConditionalZip(src2, predicate);
That's a nice one. I don't think you'll find a ready-made function directly within .net but if the operation you want is a standard action in maths I'm sure there's a library somewhere that does it. If you wanted to do it yourself, though, you could use group by. In this particular scenario:
var primes = new List<int> {2, 3, 5, 7, 11};
var numbers = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
var groups =
from number in numbers
group number by primes.First(prime => prime >= number) into gnumber
select new {
prime = gnumber.Key,
numbers = gnumber.Where(n => n != gnumber.Key)
};
That seems like a simple enough solution. It will create an enumerable of an anonoymous type with two members in it. You can transform it into a dictionary:
var dict = groups.ToDictionary(g => g.prime, g=> g.numbers);
Edit: primes has to be ordered for this to work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With