我经常需要在IEnumerable<T> A
,IEnumerable<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
}
答案 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)
这是基于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;
}
}
与版本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]