我正在寻找最有效的方法来在IEnumerable<T> B
的每一行之前对IEnumerable<T> A
中的所有行进行网格划分。Meshing IEnumerable <T>一起列表
例如:
A = {A,B}
B = {1,2,3}
啮合后:
B = {A,1 ,B,2,A,3}
我正在寻找最有效的方法来在IEnumerable<T> B
的每一行之前对IEnumerable<T> A
中的所有行进行网格划分。Meshing IEnumerable <T>一起列表
例如:
A = {A,B}
B = {1,2,3}
啮合后:
B = {A,1 ,B,2,A,3}
有没有简单的解决方案,但这样的事情可能会起作用。模数运算符在这里非常重要,用少量项目重复列表中的结果。
public static List<object> Mesh<T1, T2>(IEnumerable<T1> s1, IEnumerable<T2> s2)
{
T1[] array1 = s1.ToArray();
T2[] array2 = s2.ToArray();
int length1 = array1.Length;
int length2 = array2.Length;
int maxLength = Math.Max(length1, length2);
List<object> result = new List<object>();
for (int i = 0; i < maxLength; i++)
{
result.Add(array1[i % length1]);
result.Add(array2[i % length2]);
}
return result.
}
这实际上是唯一的正确的解决方案,但你可以把'Math.Max'放在循环之外。 –
@SebastianStehle非常好的解决方案。也许我应该使它作为扩展,只有一种类型T –
谢谢,这工作 – COBOL
试试这个,我的第一个猜测
for(int countA = 0, countB = 0; countA < A.Length; countA ++, countB++)
{
if(countB >= B.Length)
{
countB = 0;
}
newList.Add(A[countA]);
newList.Add(B[countB]);
}
该解决方案直接使用IEnumerable参数而无需初始化它们,并且还返回IEnumerable。
public IEnumerable<object> Mesh<S,T>(IEnumerable<S> items1, IEnumerable<T> items2){
bool items1Empty;
bool items2Empty;
bool items1Finished = items1Empty = ValidateParameter(items1, "items1");
bool items2Finished = items2Empty = ValidateParameter(items2, "items2");
using(var items1Enumerator = items1.GetEnumerator()){
using(var items2Enumerator = items2.GetEnumerator()){
while(true){
MoveNext(items1Enumerator, ref items1Finished);
MoveNext(items2Enumerator, ref items2Finished);
if(items1Finished && items2Finished)
break;
if(!items1Empty)
yield return items1Enumerator.Current;
if(!items2Empty)
yield return items2Enumerator.Current;
}
}
}
}
private bool ValidateParameter<T>(IEnumerable<T> parameter, string parameterName){
if(parameter == null)
throw new ArgumentNullException(parameterName);
return !parameter.Any();
}
private void MoveNext(IEnumerator enumerator, ref bool finished){
if(!enumerator.MoveNext()){
enumerator.Reset();
enumerator.MoveNext();
finished = true;
}
}
像这样的解决方案就是答案,因为问题是“最高效的”。任何包含ToArray/ToList调用的解决方案都可能不符合要求。 –
我清理了一些代码并纠正了可能发生的一些错误行为。 – dwonisch
我也想参加一个派对!因为我喜欢LINQ非常多,让我们来看看下面的LINQ溶液(确定它是不是有效的,但为了好玩):
public static void Main()
{
var a = new[] {1, 2, 3};
var b = new[] {'a', 'b'};
var joined = a.Join(
b,
x => Array.IndexOf(a, x) % b.Length,
y => Array.IndexOf(b, y) % a.Length,
(x, y) => new object[]{ x, y }
);
var flat = joined.SelectMany(x => x);
Console.Write(string.Join(", ", flat));
}
另一种方法
public static IEnumerable<T> Mesh<T>(this IEnumerable<T> source, params IEnumerable<T>[] others)
{
var enumerators = new[] { source.GetEnumerator() }.Concat(others.Select(o => o.GetEnumerator())).ToList();
var finishes = new bool[enumerators.Count];
var allFinished = false;
while (!allFinished)
{
allFinsihed = true;
for (var i = 0; i < enumerators.Count; i++)
{
IEnumerator<T> enumerator = enumerators[i];
if (!enumerator.MoveNext())
{
finishes[i] = true;
enumerator.Reset();
enumerator.MoveNext(); // Not sure, if we need it here, Reset says: BEFORE the first element
}
yield return enumerator.Current;
if (!finishes[i])
{
allFinished = false;
}
}
}
}
你可以写你自己的扩展,允许更大的灵活性
注意:这个答案不会分配大量不必要的内存,并且只枚举一次所有序列。
我已经包含参数检查。
public static IEnumerable<T> Mesh<T>(
this IEnumerable<T> source,
params IEnumerable<T>[] others)
{
if (others.LongLength == 0L)
{
foreach (var t in source)
{
yield return t;
}
yield break;
}
var nullCheck = Array.FindIndex(others, e => e == null);
if (nullCheck >= 0)
{
throw new ArgumentNullException(string.Format(
"Parameter {0} is null, this is not supported.",
++nullCheck),
(Exception)null);
}
var enumerators = new[] { source.GetEnumerator() }
.Concat(others.Select(o => o.GetEnumerator())).ToList();
try
{
var finishes = new bool[enumerators.Count];
var allFinished = false;
while (!allFinished)
{
allFinsihed = true;
for (var i = 0; i < enumerators.Count; i++)
{
if (finishes[i])
{
continue;
}
if (enumerators[i].MoveNext())
{
yield return enumerators[i].Current;
allFinished = false;
continue;
}
finishes[i] = true;
}
}
}
finally
{
foreach (var enumerator in enumerators)
{
enumerator.Dispose();
}
}
}
这将让您“网格”任意数量的锯齿序列,只要有项目有相同的类型。例如
var a = new[] { 'a', 'b', 'c' };
var b = new[] { '1', '2', '3' };
var c = new[] { 'x', 'y' };
var d = new[] { '7', '8', '9', '0' };
var meshed = a.Mesh(b, c, d);
会产生
{ 'a', '1', 'x', '7', 'b', '2', 'y', '8', 'c', '3', '9', '0' }
如果你想混合类型,你可以做
var a = new[] { 'a', 'b', 'c' };
var b = new[] { 1, 2, 3 };
var meshed = a.Cast<object>().Mesh(b.Cast<object>);
产生类似,
{ ('a'), (1), ('b'), (2), ('c'), (3) }
所以你W¯¯当一个较短的列表在一个较长的列表之前完成时,蚂蚁将环绕到开始? [MoreLINQ](https://code.google.com/p/morelinq/source/browse/MoreLinq/Interleave.cs)有一个'Interleave'操作,几乎可以实现这一点。添加一个新的'ImbalancedInterleaveStrategy'不会太困难,该新的'ImbalancedInterleaveStrategy'指出要再次回到较短的列表的开始处。 –