Склеить два массива

Обновлено: 06.07.2024

Есть несколько способов, чтобы добавить массив в массив при помощи php и все они могут пригодиться для отдельных случаев.

"Оператор +"

Это простой, но коварный способ:

Так добавляются только те ключи, которых еще нет в массиве $a. При этом элементы дописываются в конец массива.

То есть если ключ из массива $b отсутствует в массиве $a, то в результирующем массиве добавится элемент с этим ключом.
Если в массиве $a уже есть элемент с таким ключом, то его значение останется без изменений.

Иными словами от перемены мест слагаемых сумма меняется: $a + $b != $b + $a — это стоит запомнить.

А теперь более подробный пример, чтобы проиллюстрировать это:

Функция array_merge()

Использовать эту функцию можно следующим образом:

Она сбрасывает числовые индексы и заменяет строковые. Отлично подходит для того, чтобы склеить два или несколько массивов с числовыми индексами:

Если входные массивы имеют одинаковые строковые ключи, тогда каждое последующее значение будет заменять предыдущее. Однако, если массивы имеют одинаковые числовые ключи, значение, упомянутое последним, не заменит исходное значение, а будет добавлено в конец массива.

Функция array_merge_recursive

Делает то же самое, что и array_merge только еще и рекурсивно проходит по каждой ветке массива и проделывает то же самое с потомками. Подробная справка по array_merge_recursive

Функция array_replace()

Заменяет элементы массива элементами других переданных массивов. Подробная справка по array_replace.

Функция array_replace_recursive()

То же что и array_replace только обрабатывает все ветки массива. Справка по array_replace_recursive.

К стыду своему признаю, что за такими простыми вещами, несмотря на кучу лет практики, мне до сих пор приходится ходить в доку. Пожалуй запишу, как объединять массивы в PHP - вроде бы у меня "письменная память", глядишь и запомню :)

Тем более, что в PHP в этом месте есть не очевидные аспекты поведения.

Итак, PHP предлагает для объединения массивов два инструмента: php-функция array_merge и оператор "+" ("Union").

Функция array_merge()

array_merge() принимает аргументами массивы, которые нужно объединить. Магия состоит в том, как обрабатываются ключи:

  • для записей с числовым ключом значение правого массива добавляется в конец левого;
  • для записей со строковым ключом значение правого массива перезаписывает значение левого с аналогичным ключом, если оно есть, если нет - добавляется.

Соответственно, array_merge() можно использовать для конкатенации индексных (indexed) массивов и слияния ассоциативных (associative).

Если в сливаемых массивах есть ключи разных типов, результат оказывается зачастую непредсказуемым.

Оператор сложения массивов (Union): $arr1 + $arr2

Поведение описано вот тут. Принцип такой:

То есть идея оператора в том, чтобы добавить в левый массив новые значения из правого. Редко когда бывает нужно, но лучше помнить про такую возможность.

Конкатенация (объединение) массивов

Как видим, ни один из этих инструментов не позволяет выполнить конкатенацию массивов. В статье вики по предыдущей ссылке есть требование:

Из-за объединения по ключам и array_merge(), и union могут вернуть результат произвольной длины.

Я нафигачил вот такой нехитрый метод, тупо добавляющий каждый правый массив в конец результата без сохранения ключей:

Можно пользоваться по необходимости. Но вообще лучше избегать массивов со смешанными ключами, инфа 100% :)

А вот табличка, демонстрирующая три описанных выше метода:

Вот так. Если, прочитав это, вы решили срочно сварить немного коррозионностойкой стали, или например кусок углеродистой стали с титаном, ваш выбор - это аргонодуговая сварка электродом, ничего лучше не найдете, поскольку вольфрамовый электрод - это сила!

вы можете принять вызов ToArray () от конца. Есть ли причина, по которой вам нужно, чтобы это был массив после вызова Concat?

вызов Concat создает итератор для обоих массивов. Он не создает новый массив, поэтому вы не использовали больше памяти для нового массива. Когда вы вызываете ToArray, вы фактически создаете новый массив и занимаете память для нового массива.

поэтому, если вам просто нужно легко перебирать оба, просто вызовите Concat.

Я знаю, что OP было только слегка любопытно о производительности. Что большие массивы могут получить другой результат (см. @kurdishTree). И что это обычно не имеет значения (@Иордании.народный.) Тем не менее, мне было любопытно, и поэтому я сошел с ума ( как объяснял @TigerShark). Я имею в виду, что я написал простой тест, основанный на исходном вопросе. и все ответы.

enter image description here

сверните свои собственные победы.

будет неэффективным для больших массивов. Это означает Concat метод предназначен только для массивов размера meduim (до 10000 элементов).

более эффективно (быстрее) использовать Buffer.BlockCopy над Array.CopyTo ,

Я написал простую тестовую программу, которая "разогревает дрожание", скомпилированную в режиме выпуска и запустила ее без отладчика на моей машине.

для 10,000,000 итераций примера в вопросе

функция concat взял 3088ms

копировать, чтобы взял 1079ms

BlockCopy взял 603ms

если я изменю тестовые массивы на две последовательности от 0 до 99, то я получу результаты, подобные этому,

функция concat взял 45945ms

копировать, чтобы взял 2230ms

BlockCopy взял 1689ms

из этих результатов я могу утверждать, что CopyTo и BlockCopy методы значительно эффективнее, чем Concat и кроме того, если производительность является целью, BlockCopy имеет значение CopyTo .

чтобы предостеречь этот ответ, если производительность не имеет значения, или будет несколько итераций, выберите метод, который вы найдете проще всего. Buffer.BlockCopy предлагает некоторую утилиту для преобразования типов за пределами этого вопроса.

вы можете сделать это, как вы упомянули, или если вы хотите получить действительно инструкцию об этом вы можете свернуть свой собственный цикл:

наиболее эффективной структурой с точки зрения ОЗУ (и ЦП) для хранения объединенного массива будет специальный класс, реализующий IEnumerable (или, если вы хотите, даже производный от массива) и внутренне связанный с исходными массивами для чтения значений. AFAIK Concat делает именно это.

в вашем примере кода Вы можете опустить .ToArray () хотя, что сделало бы его более эффективным.

вам нужно помнить, что при использовании LINQ вы используете отложенное выполнение. Другие методы, описанные здесь, работают отлично, но они выполняются немедленно. Кроме того, функция Concat (), вероятно, оптимизирована так, как вы не можете сделать сами (вызовы внутренних API, вызовы ОС и т. д.). В любом случае, если вам действительно не нужно пытаться оптимизировать, вы находитесь на своем пути к "корню всего зла" ;)

Я нашел элегантное решение для одной линии, используя LINQ или лямда - expression, оба работают одинаково (LINQ преобразуется в лямбда при компиляции программы). Решение работает для любого типа массива и для любого количества массивов.

использование LINQ:

С Помощью Лямбда:

Я предоставил оба по своему усмотрению. Производительность мудрый @Сергей Штейн это или @deepee1 это!--4--> решения немного быстрее, лямбда-выражение является самым медленным. Затраченное время зависит от типа(ов) элементов массива, но если нет миллионов вызовов, нет существенной разницы между методами.

извините, чтобы оживить старую нить, но как насчет этого:

тогда в вашем коде:

пока ты не позвонишь .ToArray() , .ToList() или .ToDictionary(. ) , память не выделяется, вы можете "построить свой запрос" и либо вызвать один из этих трех, чтобы выполнить его, либо просто пройти через них все с помощью foreach (var i in z) <. >предложение, которое возвращает элемент за раз из yield return t; выше.

вышеуказанную функцию можно сделать в расширение как следует:

так в коде, вы можете сделать что-то вроде:

остальное то же самое, что и раньше.

еще одним улучшением к этому было бы изменение T[] на IEnumerable (т. е. params T[][] станет params IEnumerable [] ), чтобы эти функции принимали больше, чем просто массивы.

надеюсь, что это помогает.

для int[] то, что вы сделали, выглядит хорошо для меня. astander это ответ также будет хорошо работать для List .

Все примеры будут приведены для PHP версии 7.1.8:

Допустим, есть изначальные массивы:

Как видите, во всех способах получились разные итоговые массивы. Почему получается именно так? Для начала давайте посмотрим как PHP интерпретирует исходные массивы:

Во-первых, всем значениям без ключей присваиваются числовые ключи. Однако, присвоенный таким образом ключ может быть перезаписан следующим значением с объявленным равнозначным ключём. Но в данном случае это не влияет на конечный результат слияния массивов. Если вкратце, то array_merge затирает строковые ключи значениями из последующих массивов, но значения с числовыми ключами добавляет в конец массива и присваивает им новые ключи!

Теперь давайте заглянем в документацию.

Если входные массивы для array_merge() имеют одинаковые строковые ключи, тогда каждое последующее значение будет заменять предыдущее. Однако, если массивы имеют одинаковые числовые ключи, значение, упомянутое последним, не заменит исходное значение, а будет добавлено в конец массива.

array_replace() замещает значения первого массива значениями с такими же ключами из других переданных массивов. Если ключ из первого массива присутствует во втором массиве, его значение заменяется на значение из второго м ассива. Если ключ есть во втором массиве, но отсутствует в первом — он будет создан в первом массиве. Если ключ присутствует только в первом массиве, то сохранится как есть. Если для замены передано несколько массивов, они будут обработаны в порядке передачи и последующие массивы будут перезаписывать значения из предыдущих.

Достаточно интересная и не самая редкая задача. Допустим, есть несколько массивов с разными значениями, например: массив городов, возрастов, полов и…

Стандартные библиотеки PHP умеют генерировать только целые случайные числа. Однако, возникают задачи где нужно не целое рандомное число с максимально…

Иногда при обработке с помощью PHP больших и не очень данных, можно словить досадную ошибку посреди выполнения скрипта: PHP Fatal…

Читайте также: