将几个IEnumerable <t>提要混合到C#</t>中的一个

时间:2014-08-22 12:33:15

标签: c# .net linq ienumerable enumerable

我经常需要在IEnumerable<T> AIEnumerable<T> B执行操作,我想创建IEnumerable<T> C,其中C将从A获取2T,然后从B获取1T,然后再从2T开始A,然后是B的1T等等 - AABAABAAB ......

C#中是否有一些语言构造,可能是一些LINQ表达式可以很容易地完成。

现在我写了一个帮助我的小助手类:

var C = EnumerableMixer(new int[]{2, 1}, A, B)

    public class EnumerableMixer<T> : IEnumerable<T>
    {
        int[] Quantity;
        IEnumerable<T>[] Args;

        public EnumerableMixer(int[] quantity, params IEnumerable<T>[] args)
        {
            this.Quantity = quantity;
            this.Args = args;
            if (quantity.Length != args.Length)
                throw new NotImplementedException("Quantity must have same length as number of args!");
        }

        IEnumerable<T> Mix
        {
            get
            {
                var available = new List<int>(Quantity.Length);
                var enumerators = new List<IEnumerator<T>>(Quantity.Length);
                for (int i = 0; i < Quantity.Length; ++i)
                {
                    available.Add(i);
                    enumerators.Add(Args[i].GetEnumerator());
                }
                while (available.Count > 0)
                {
                    for (int i = 0; i < available.Count; ++i)
                    {
                        var id = available[i];
                        for (int j = 0; j < Quantity[id]; ++j)
                        {
                            if (enumerators[id].MoveNext())
                                yield return enumerators[id].Current;
                            else
                            {
                                available.RemoveAt(i);
                                i--;
                                break;
                            }
                        }
                    }
                }
            }
        }

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return Mix.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion
    }

3 个答案:

答案 0 :(得分:2)

稍微长一点的解决方案是:

    public IEnumerable<TValue> Mix<TValue>(IEnumerable<TValue> a, IEnumerable<TValue> b)
    {
        var aEnumerator = a.GetEnumerator();
        var bEnumerator = b.GetEnumerator();

        while (true)
        {
            if (!aEnumerator.MoveNext())
            {
                yield break;
            }

            yield return aEnumerator.Current;

            if (!aEnumerator.MoveNext())
            {
                yield break;
            }

            yield return aEnumerator.Current;

            if (!bEnumerator.MoveNext())
            {
                yield break;
            }

            yield return bEnumerator.Current;
        }
    }

此解决方案不会创建不必要的对象。如果您不熟悉yield关键字:http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

答案 1 :(得分:2)

版本1

这是基于adhie的解决方案。它考虑了比率并在退出方法之前完全完成了两个序列。 flag用于跟踪最后一个循环是否产生了任何结果。当flag为假时,两个序列都已耗尽。

public IEnumerable<T> Mix<T>(IEnumerable<T> sequenceA, IEnumerable<T> sequenceB, int ratioA, int ratioB)
{
    var etorA = sequenceA.GetEnumerator();
    var etorB = sequenceB.GetEnumerator();
    bool flag = true;

    while(flag)
    {
        flag = false;

        for(int i = 0; i < ratioA && (flag |= etorA.MoveNext()); i++)
            yield return etorA.Current;

        for(int i = 0; i < ratioB && (flag |= etorB.MoveNext()); i++)
            yield return etorB.Current;
    }
}

第2版

与版本1几乎相同,但采用可变数量的序列。

public IEnumerable<T> Mix<T>(params KeyValuePair<IEnumerable<T>, uint>[] quantifiedSequences)
{
    var sequences = quantifiedSequences.Select(x => new { Etor = x.Key.GetEnumerator(), Quantity = x.Value });
    bool flag = true;

    while(flag)
    {
        foreach (var sequence in sequences)
            for(int i = 0; i < sequence.Quantity && (flag |= sequence.Etor.MoveNext()); i++)
                yield return sequence.Etor.Current;
    }
}

答案 2 :(得分:1)

这是我的Linq解决方案:

对于每个元素,分配一个索引,然后按正确的顺序获取元素。

// for example. You can use with any type
IEnumerable<Int32> listA = new List<Int32>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
IEnumerable<Int32> listB = new List<Int32>() { 101, 102, 103, 104, 105, 106, 107, 108, 109, 110};

// you want 2 of list A and 1 of list B. You can change the values
Int16[] quantity = new Int16[] { 2, 1 };

IEnumerable<Int32> listC = listA.Select((e, i) => new  { element = e, index = (i / (quantity[0]) * (quantity[0] + quantity[1]) + (i%quantity[0]))})
                                .Concat(listB.Select((e, i) => new  { element = e, index = (i / (quantity[1]) * (quantity[0] + quantity[1]) + (i%quantity[1]) + quantity[0])}))
                                .OrderBy(e => e.index)
                                .Select(e => e.element);

结果是IEnumerable<Int32>:1,2,101,3,4,102,5,6,103,7,8,104 ......

困难的是确定指数。它可以满足您的需求({2,1},{3,2},...)

第一个清单:

index = i/(quantity[0]) * (quantity[0]+quantity[1]) + (i%quantity[0]) + 0

第二个清单:

index = i/(quantity[1]) * (quantity[0]+quantity[1]) + (i%quantity[1]) + quantity[0]