如何合并一个分为2个数组的数组与奇数和偶数索引?

时间:2018-05-13 07:28:54

标签: c# linq collections

从这个Q: How to split an array to 2 arrays with odd and even indices respectively?

我已经使用这种方法将数组分成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();

这导致2个阵列:

aEven : {1, 7}
aOdd  : {3, 8}

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

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

aEven / aOdd输入所需的输出是:

{ 1, 3, 7, 8 };

5 个答案:

答案 0 :(得分:4)

执行此操作的一种方法是将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}]");

这仅适用于你的场景但是(拆分然后通过奇数/偶数索引恢复数组,假设'子阵列'的顺序有一直保持)。

结果数组将具有相同的大小(原始数组具有偶数项)或偶数数组将具有一个额外项(原始数组具有奇数项)。在后一种情况下,额外项目将被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];

另一种方法是使用ZipSelectManyConcat的组合(以说明最后一项):

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();

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

答案 1 :(得分:2)

只需使用for循环。它不会创建所有这些元组的中间数组,同时具有可比性,并且需要相同数量的代码。

int[] Merge(int[] evenItems, int[] oddItems)
{
    var itemsCount = evenItems.Length + oddItems.Length;
    var result = new int[itemsCount];

    for (var i = 0; i < itemsCount; i++)
    {
        var sourceIndex = Math.DivRem(i, 2, out var remainder);
        var source = remainder == 0 ? evenItems : oddItems;
        result[i] = source[sourceIndex];
    }

    return result;
}

答案 2 :(得分:1)

创建InterlockWith扩展方法,是的,循环无法逃脱:

public static IEnumerable<T> InterlockWith<T>(this IEnumerable<T> seq1, IEnumerable<T> seq2)
    {
        Tuple<T[], int>[] metaSequences = new Tuple<T[], int>[2];
        metaSequences[0] = Tuple.Create(seq1.ToArray(), seq1.Count());
        metaSequences[1] = Tuple.Create(seq2.ToArray(), seq2.Count());
        var orderedMetas = metaSequences.OrderBy(x => x.Item2).ToArray();

        for (int i = 0; i < orderedMetas[0].Item2; i++)
        {
            yield return metaSequences[0].Item1[i];
            yield return metaSequences[1].Item1[i];
        }

        var remainingItems = orderedMetas[1].Item1.Skip(orderedMetas[0].Item2);
        foreach (var item in remainingItems)
        {
            yield return item;
        }
    }

您可以通过以下方式调用结果集来获取结果集:

a = aEven.InterlockWith(aOdd).ToArray();

答案 3 :(得分:1)

var merged = evens
  .Zip(odds, (even, odd) => new [] {even, odd})
  .SelectMany(pair => pair)
  .Concat(evens.Skip(odds.Length))
  .ToArray();

答案 4 :(得分:-1)

首先你可以使用Concat合并它们,然后如果你想要对reslt进行排序,你可以使用OrderBy:

aEven.Concat(aOdd).OrderBy(b => b).ToArray();