如何合并被拆分为具有奇数和偶数索引的 2 个数组的数组?

zig*_*zig 3 c# linq collections

来自这个 Q: 如何将一个数组拆分为 2 个分别具有奇数和偶数索引的数组?

我使用此方法将数组拆分为 2 个数组,分别具有奇数和偶数索引:

int[] a = new int[] { 1, 3, 7, 8 };

int[] aEven = a.Where((x, i) => i % 2 == 0).ToArray();
int[] aOdd = a.Where((x, i) => i % 2 != 0).ToArray();
Run Code Online (Sandbox Code Playgroud)

这导致 2 个数组:

aEven : {1, 7}
aOdd  : {3, 8}
Run Code Online (Sandbox Code Playgroud)

如何以优雅的方式将aEven/合并aOdd回与原始a数组相同的位置?

注意:我不会更改aEven/aOdd数组,并且保证两个数组的长度相同。

我需要从aEven/aOdd输入的输出是:

{ 1, 3, 7, 8 };
Run Code Online (Sandbox Code Playgroud)

pin*_*x33 5

做到这一点的一种方法是结合Enumerable.ZipEnumerable.Aggregate加上一点技巧。请注意,这仍然在引擎盖下使用循环。

var aList = aEven.Zip(aOdd, (even, odd) => new {even, odd})
                 .Aggregate(new List<int>(aEven.Length + aOdd.Length),
                            (list, z) =>
                            {
                                list.Add(z.even);
                                list.Add(z.odd);
                                return list;
                            });
if (aEven.Length != aOdd.Length)
    aList.Add(aEven[aEven.Length-1]);

var aOutput = aList.ToArray();

for (var i = 0; i < aOutput.Length; ++i)
    Console.WriteLine($"aOutput[{i}] ==> {aOutput[i]} == {a[i]} <== a[{i}]");
Run Code Online (Sandbox Code Playgroud)

但是,这仅适用于您的场景(假设已保持“子数组”的顺序,则通过奇数/偶数索引拆分然后恢复数组)。

结果数组要么具有相同的大小(原始数组有偶数个项目),要么偶数数组将有一个额外的项目(原始数组有奇数个项目)。在后一种情况下,额外的项目将被丢弃Zip并需要手动计算。这不适用于通过其他方式计算两个子数组的其他场景。

您也可以在没有中间列表的情况下使用预先分配的数组来执行此操作,但您必须跟踪 LINQ 调用之外的索引(我不太喜欢):

var index = 0;
var aOutput = aEven.Zip(aOdd, (even, odd) => new {even, odd})
                   .Aggregate(new int[aEven.Length + aOdd.Length],
                             (arr, z) =>
                             {
                                 arr[index++] = z.even;
                                 arr[index++] = z.odd;
                                 return arr;
                             });
if (aEven.Length != aOdd.Length)
    aOutput[index] = aEven[aEven.Length-1];
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用Zip,SelectManyConcat(考虑最后一项)的组合:

var aOutput = aEven.Zip(aOdd, (even, odd) => new[]{ even, odd })
    .SelectMany(z => z)
    .Concat(aEven.Length == aOdd.Length ? new int[0] : new []{ aEven[aEven.Length - 1] })
    .ToArray();
Run Code Online (Sandbox Code Playgroud)

一个简单的 for 循环仍然可能是最简单的解决方案。