Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there already a Conditional Zip function in c#?

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]}
like image 844
Adam Mills Avatar asked Sep 19 '12 13:09

Adam Mills


2 Answers

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);
like image 70
max Avatar answered Nov 17 '22 17:11

max


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.

like image 34
Evren Kuzucuoglu Avatar answered Nov 17 '22 15:11

Evren Kuzucuoglu