kenwilcox / morelinq

Automatically exported from code.google.com/p/morelinq
Apache License 2.0
0 stars 0 forks source link

Interleave irregular enumerables with circular strategy #89

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
result: !a1!b2!c3!a4!b5!c1!a2!b3!c4!a5!b1!c2!a3!b4!c5!a1... (Hope I got that 
right, you get the idea). The length of the IEnumerables is unknown of course 
and the result should be lazy-evaluated.

There may already be a way to do this but I didn't see anything obvious like 
this:

public static IEnumerable<T> Interleave<T>(params IEnumerable<T>[] 
sequencesToInterleave)
{
 ... 
}

foreach (var item in Interleave(new string[] { "!" }, new string[] { "a", "b", 
"c" }, new string[] { "1", "2", "3", "4", "5" }))
{
                    Console.Write(item.ToString());
}

Original issue reported on code.google.com by virtan...@gmail.com on 9 May 2014 at 12:18

GoogleCodeExporter commented 9 years ago
Here's my first attempt at this. It produces the result atleast.

            public IEnumerable<T> Interleave<T>(params IEnumerable<T>[] sequencesToInterleave)
            {
                var ls = sequencesToInterleave;
                var lsl = ls.Length;
                List<IEnumerator<T>> ies = new List<IEnumerator<T>>(lsl);
                for (int i = 0; i < lsl; i++)
                {
                    var en = ls[i].GetEnumerator();
                    ies.Add(en);
                    //en.MoveNext();
                }
                while (true)
                {
                    for (int i = 0; i < lsl; i++)
                    {
                        var e=ies[i];
                        if (!e.MoveNext()) { e.Reset(); if (!e.MoveNext()) continue; }
                        yield return e.Current;
                    }
                }
            }

Original comment by virtan...@gmail.com on 9 May 2014 at 1:06

GoogleCodeExporter commented 9 years ago
This variation should stop the yielding once the longest enumerable reaches its 
end... Not sure I'm happy with how it returns 6 of the non-numeric chars for 
the test above while the longest enum has 5 numeric chars. I don't know how to 
fix this yet.

        public static IEnumerable<T> InterleaveForLongest<T>(params IEnumerable<T>[] sequencesToInterleave)
        {
            var ls = sequencesToInterleave;
            var lsl = ls.Length;
            bool[] resets = new bool[lsl];
            List<IEnumerator<T>> ies = new List<IEnumerator<T>>(lsl);
            for (int i = 0; i < lsl; i++) ies.Add(ls[i].GetEnumerator());
            while (true)
            {
                for (int i = 0; i < lsl; i++)
                {
                    var e = ies[i];
                    if (!e.MoveNext()) {
                        e.Reset(); resets[i] = true;
                        if (resets.All(x => x == true)) yield break;
                        if (!e.MoveNext()) continue;
                    }
                    yield return e.Current;
                }
            }
        }

Original comment by virtan...@gmail.com on 9 May 2014 at 1:23

GoogleCodeExporter commented 9 years ago
Here's the final code I ended up with for now. I needed to group some data by 
custom key and then interleave the values of each group...

var g = data.GroupBy(x => x.type);
foreach (var item in InterleaveForLongestG(g)) 
Console.WriteLine(item.ToString());

        public static IEnumerable<T> InterleaveForLongestG<K, T>(IEnumerable<IGrouping<K, T>> sequencesToInterleave)
        {
            var ls = sequencesToInterleave.ToList();
            var lsl = ls.Count;
            bool[] resets = new bool[lsl];
            List<IEnumerator<T>> ies = new List<IEnumerator<T>>(lsl);
            for (int i = 0; i < lsl; i++) ies.Add(ls[i].ToList().GetEnumerator());
            while (true)
            {
                for (int i = 0; i < lsl; i++)
                {
                    var e = ies[i];
                    if (!e.MoveNext())
                    {
                        e.Reset(); resets[i] = true;
                        if (resets.All(x => x == true)) yield break;
                        if (!e.MoveNext()) continue;
                    }
                    yield return e.Current;
                }
            }
        }

Original comment by virtan...@gmail.com on 9 May 2014 at 1:58

GoogleCodeExporter commented 9 years ago
Turns out I also needed a predicate for more control. Here's hopefully the 
final things I ended up using.

        public static IEnumerable<T> EnumInfiniteInterleaved<T>(Func<T, bool> predicate, params IEnumerable<T>[] sequencesContractoInterleave)
        {
            var ls = sequencesContractoInterleave;
            var lsl = ls.Length;
            if (lsl < 2) throw new ArgumentOutOfRangeException("Atleast two sequences required");
            List<IEnumerator<T>> ies = new List<IEnumerator<T>>(lsl);
            for (int i = 0; i < lsl; i++) ies.Add(ls[i].GetEnumerator());
            while (true)
            {
                for (int i = 0; i < lsl; i++)
                {
                    var e = ies[i];
                nxt:
                    if (!e.MoveNext()) { e.Reset(); if (!e.MoveNext()) continue; }
                    if (!predicate(e.Current)) goto nxt;
                    yield return e.Current;
                }
            }
        }
        public static IEnumerable<T> EnumInfinite<T>(Func<T, bool> predicate, IEnumerable<T> source)
        {
            if (source == null) yield break; // be a gentleman
            IEnumerator<T> e = source.GetEnumerator();

        iterateAllAndBackToStart:
            while (e.MoveNext())
            {
                var c = e.Current;
                if (predicate(e.Current)) yield return c;
            }
            e.Reset();
            goto iterateAllAndBackToStart;
        }

Original comment by virtan...@gmail.com on 9 May 2014 at 1:40