Как объединить массив, который был разбит на 2 массива с нечетными и четными индексами? - PullRequest
0 голосов
/ 13 мая 2018

Из этого 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();

В результате получается 2 массива:

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

Как можно элегантно объединить обратно aEven / aOdd, чтобы он был таким же, как исходный массив a?

ПРИМЕЧАНИЕ. Я не изменяю массивы aEven / aOdd, и гарантировано оба массива имеют одинаковую длину.

Выходной сигнал, который мне нужен от aEven / aOdd входов:

{ 1, 3, 7, 8 };

Ответы [ 6 ]

0 голосов
/ 13 мая 2018
public static IEnumerable<T> MergeByZipAndRemainder<T>(T[] this firsts, T[] seconds)
{
  int maxLength = Math.Max(firsts.Length, seconds.Length);
  foreach(int i in Enumerable.Range(0, maxLength))
  {
     if (i < firsts.Length) { yield return firsts[i]; }
     if (i < seconds.Length) { yield return seconds[i]; }
  }
}

Тогда:

var result = evens.MergeByZipAndRemainder(odds).ToArray();

Этот подход встречает критику со стороны ответчиков.

  • Это элегантно.
  • Он обращается кобщий случай объединения массивов, длина которых может сильно различаться.
  • Перечисляет только один раз O (N + M).
  • Он не выделяет промежуточные объекты массива, которые потребуют сборку мусора.
  • Это должно быть быстрее на бумаге, но я не измерял производительность.
0 голосов
/ 13 мая 2018
var merged = evens
  .Zip(odds, (even, odd) => new [] {even, odd})
  .SelectMany(pair => pair)
  .Concat(evens.Skip(odds.Length))
  .ToArray();
0 голосов
/ 13 мая 2018

Просто используйте вместо этого for loop. Он не создаст все эти промежуточные массивы кортежей, хотя он сравнительно удобен для чтения и требует столько же кода.

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;
}
0 голосов
/ 13 мая 2018

Создать 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();
0 голосов
/ 13 мая 2018

Один из способов сделать это - объединить Enumerable.Zip и Enumerable.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];

Еще один способ сделать это - использовать комбинацию Zip, SelectMany и Concat (для учета последнего элемента):

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

Простой цикл for, вероятно, все еще будет самым простым решением.

0 голосов
/ 13 мая 2018

Сначала вы можете объединить их с помощью Concat, а затем, если вы хотите отсортировать результат, вы можете использовать OrderBy:

aEven.Concat(aOdd).OrderBy(b => b).ToArray();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...