PHP добавляет один массив к другому (не array_push или +)

278

Как добавить один массив в другой, не сравнивая их ключи?

$a = array( 'a', 'b' );
$b = array( 'c', 'd' );

В конце это должно быть: Array( [0]=>a [1]=>b [2]=>c [3]=>d ) Если я использую что-то вроде []или array_push, это вызовет один из следующих результатов:

Array( [0]=>a [1]=>b [2]=>Array( [0]=>c [1]=>d ) )
//or
Array( [0]=>c [1]=>d )

Это должно быть что-то, делающее это, но более элегантно:

foreach ( $b AS $var )
    $a[] = $var;
Данил К
источник
16
array_merge ($a, $b)должен делать именно то, что вы хотите, по крайней мере, с PHP 5+.
tloach
1
(связанный) + Оператор для Массива в PHP
Гордон
6
ни один из опубликованных вами array_merge();результатов не array_merge();должен быть точным, что вам нужно:print_r(array_merge($a,$b)); // outputs => Array ( [0] => a [1] => b [2] => c [3] => d )
acm
2
Я полностью не согласен с термином «добавить». Добавление действительно означает, что элементы одного массива становятся элементами другого (целевого) массива, который может уже иметь некоторые элементы, поэтому изменяется целевой массив. Объединение выделяет новый массив и элементы COPIES обоих массивов, тогда как добавление фактически означает повторное использование элементов массива назначения без дополнительного выделения памяти.
Тишма

Ответы:

425

array_merge это элегантный способ:

$a = array('a', 'b');
$b = array('c', 'd');
$merge = array_merge($a, $b); 
// $merge is now equals to array('a','b','c','d');

Делать что-то вроде:

$merge = $a + $b;
// $merge now equals array('a','b')

Не будет работать, потому что +оператор фактически не объединяет их. Если у них $aте же ключи, что $bи у него, он ничего не сделает.

netcoder
источник
16
Только будьте осторожны, если ваши ключи не цифры, а строки. Из документа: Если входные массивы имеют одинаковые строковые ключи, то более позднее значение для этой клавиши перезапишет предыдущее
Dusan Plavak
или использовать современный пейнтбольный оператор как @bstoney ответ stackoverflow.com/a/37065301/962634
базилик
76

Другой способ сделать это в PHP 5.6+ - использовать ...токен.

$a = array('a', 'b');
$b = array('c', 'd');

array_push($a, ...$b);

// $a is now equals to array('a','b','c','d');

Это также будет работать с любым Traversable

$a = array('a', 'b');
$b = new ArrayIterator(array('c', 'd'));

array_push($a, ...$b);

// $a is now equals to array('a','b','c','d');

Предупреждение , хотя:

  • в версиях PHP до 7.3 это приведет к фатальной ошибке, если $bмассив пустой или не проходимый, например, не массив
  • в PHP 7.3 будет выдано предупреждение, если $bоно не проходимо
bstoney
источник
Какой термин используется для такого синтаксиса? (Например, в JS это называется оператором распространения) Или вы можете предоставить ссылку на документацию?
базилик
3
@basil вы найдете ...обычно splat operatorв php.
mickmackusa
Наиболее полезный ответ при поиске простого способа добавления массива к себе без переопределения любых предыдущих элементов.
Даниэль
1
array_pushпринимает один аргумент начиная с php 7.3, что предотвращает ошибки с пустыми массивами.
vctls
на самом деле это самый элегантный и эффективный способ. спасибо
Хасан Али Салем
33

Почему бы не использовать

$appended = array_merge($a,$b); 

Почему вы не хотите использовать этот, правильный, встроенный метод.

Марк Бейкер
источник
Где OP говорит, что он "не хочет использовать" array_merge () ...?
KittenCodings
3
@KittenCodings - прочитайте «историю изменений» вопроса ... исходный вопрос был озаглавлен PHP append one array to another (not array_merge or array_push)... впоследствии изменен на, PHP append one array to another (not array_merge or +)прежде чем сменить его на текущее название
Марк Бейкер
2
@MarkBaker Вау! Я не знал, что ТАК имеет историю редактирования! Извините за это, и спасибо, это сильно меняется и в некоторой степени мешает модераторам вводить слова в уста людей. Ранее я чувствовал, что некоторые вопросы были искажены, а их комментарии аннулированы контентом, удаленным / отредактированным, хотя я думаю, что большинство людей, вероятно, не читает история редактирования, я уверен, что, черт возьми, будет
впредь
21

Это довольно старый пост, но я хочу добавить кое-что о добавлении одного массива в другой:

Если

  • один или оба массива имеют ассоциативные ключи
  • ключи обоих массивов не имеют значения

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

array_merge(array_values($array), array_values($appendArray));

array_merge не объединяет числовые ключи, поэтому добавляет все значения $ appendArray. При использовании нативных функций php вместо цикла foreach, он должен быть быстрее на массивах с большим количеством элементов.

Добавление 2019-12-13: Начиная с PHP 7.4, есть возможность добавлять или добавлять массивы в виде оператора Array Spread Operator:

    $a = [3, 4];
    $b = [1, 2, ...$a];

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

    $a = ['a' => 3, 'b' => 4];
    $b = ['c' => 1, 'a' => 2, ...$a];

«Неустранимая ошибка: неперехваченная ошибка: невозможно распаковать массив со строковыми ключами»

    $a = [3 => 3, 4 => 4];
    $b = [1 => 1, 4 => 2, ...$a];

array (4) {[1] => int (1) [4] => int (2) [5] => int (3) [6] => int (4)}

    $a = [1 => 1, 2 => 2];
    $b = [...$a, 3 => 3, 1 => 4];

array (3) {[0] => int (1) [1] => int (4) [3] => int (3)}

SenseException
источник
1
Это также должно иметь преимущество, оставляя входные массивы нетронутыми.
Джон Суррелл
1
Да, на всякий случай более безопасно извлечь array_values, чтобы вы не слились в одни и те же ключи.
Габриэль Родригес
15
<?php
// Example 1 [Merging associative arrays. When two or more arrays have same key
// then the last array key value overrides the others one]

$array1 = array("a" => "JAVA", "b" => "ASP");
$array2 = array("c" => "C", "b" => "PHP");
echo " <br> Example 1 Output: <br>";
print_r(array_merge($array1,$array2));

// Example 2 [When you want to merge arrays having integer keys and
//want to reset integer keys to start from 0 then use array_merge() function]

$array3 =array(5 => "CSS",6 => "CSS3");
$array4 =array(8 => "JAVASCRIPT",9 => "HTML");
echo " <br> Example 2 Output: <br>";
print_r(array_merge($array3,$array4));

// Example 3 [When you want to merge arrays having integer keys and
// want to retain integer keys as it is then use PLUS (+) operator to merge arrays]

$array5 =array(5 => "CSS",6 => "CSS3");
$array6 =array(8 => "JAVASCRIPT",9 => "HTML");
echo " <br> Example 3 Output: <br>";
print_r($array5+$array6);

// Example 4 [When single array pass to array_merge having integer keys
// then the array return by array_merge have integer keys starting from 0]

$array7 =array(3 => "CSS",4 => "CSS3");
echo " <br> Example 4 Output: <br>";
print_r(array_merge($array7));
?>

Вывод:

Example 1 Output:
Array
(
[a] => JAVA
[b] => PHP
[c] => C
)

Example 2 Output:
Array
(
[0] => CSS
[1] => CSS3
[2] => JAVASCRIPT
[3] => HTML
)

Example 3 Output:
Array
(
[5] => CSS
[6] => CSS3
[8] => JAVASCRIPT
[9] => HTML
)

Example 4 Output:
Array
(
[0] => CSS
[1] => CSS3
)

Ссылочный исходный код

Хасан Амир Хан
источник
12

Для большого массива лучше объединить без array_merge, чтобы избежать копирования памяти.

$array1 = array_fill(0,50000,'aa');
$array2 = array_fill(0,100,'bb');

// Test 1 (array_merge)
$start = microtime(true);
$r1 = array_merge($array1, $array2);
echo sprintf("Test 1: %.06f\n", microtime(true) - $start);

// Test2 (avoid copy)
$start = microtime(true);
foreach ($array2 as $v) {
    $array1[] = $v;
}
echo sprintf("Test 2: %.06f\n", microtime(true) - $start);


// Test 1: 0.004963
// Test 2: 0.000038
Снарк
источник
Работает как шарм, для меня этот подход был в 50 раз быстрее.
luttkens
9

Исходя из ответов bstoney и Snark, я провел несколько тестов по различным методам:

// Test 1 (array_merge)
$array1 = $array2 = array_fill(0, 50000, 'aa');
$start = microtime(true);
$array1 = array_merge($array1, $array2);
echo sprintf("Test 1: %.06f\n", microtime(true) - $start);

// Test2 (foreach)
$array1 = $array2 = array_fill(0, 50000, 'aa');
$start = microtime(true);
foreach ($array2 as $v) {
    $array1[] = $v;
}
echo sprintf("Test 2: %.06f\n", microtime(true) - $start);

// Test 3 (... token)
// PHP 5.6+ and produces error if $array2 is empty
$array1 = $array2 = array_fill(0, 50000, 'aa');
$start = microtime(true);
array_push($array1, ...$array2);
echo sprintf("Test 3: %.06f\n", microtime(true) - $start);

Который производит:

Test 1: 0.002717 
Test 2: 0.006922 
Test 3: 0.004744

ОРИГИНАЛ: Я полагаю, что в PHP 7 метод 3 является значительно лучшей альтернативой из-за того, как теперь работают циклы foreach , то есть для создания копии итеративного массива.

Хотя метод 3 не является строго ответом на критерии «не array_push» в вопросе, это одна строка и самая высокая производительность во всех отношениях, я думаю, что вопрос задавался до того, как синтаксис ... был опцией.

ОБНОВЛЕНИЕ 25/03/2020: я обновил тест, который был ошибочным, поскольку переменные не были сброшены. Интересно (или запутанно), что результаты теперь показывают, что тест 1 был самым быстрым, где он был самым медленным, пройдя от 0,008392 до 0,002717! Это может быть связано только с обновлениями PHP, поскольку на это не повлиял бы недостаток тестирования.

Итак, сага продолжается, теперь я начну использовать array_merge!

Джейми Робинсон
источник
2
Вы не сбрасываете array1 перед каждым тестом, поэтому каждый тест содержит на 50 000 элементов больше, чем предыдущий.
Дакусан
Удивительно, что после стольких лет вы стали первым, кто поднимет меня на это, спасибо, я скоро проведу повторную проверку :)
Джейми Робинсон
5

Начиная с PHP 7.4 вы можете использовать ... оператор . Это также известно как оператор splat в других языках, включая Ruby.

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
var_dump($fruits);

Вывод

array(5) {
    [0]=>
    string(6) "banana"
    [1]=>
    string(6) "orange"
    [2]=>
    string(5) "apple"
    [3]=>
    string(4) "pear"
    [4]=>
    string(10) "watermelon"
}

Оператор Splat должен иметь лучшую производительность, чем array_merge . Это связано не только с тем, что оператор splat является языковой структурой, а array_merge является функцией, но также с тем, что оптимизация времени компиляции может быть выполнена для постоянных массивов.

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

$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$arr3 = [...$arr1, ...$arr2];
$arr4 = [...$arr1, ...$arr3, 7, 8, 9];
dtar
источник
3

До PHP7 вы можете использовать:

array_splice($a, count($a), 0, $b);

array_splice()работает со ссылкой на массив (1-й аргумент) и помещает значения массива (4-й аргумент) вместо списка значений, начинающегося со 2-го аргумента и номера 3-го аргумента. Когда мы устанавливаем 2-й аргумент как конец массива источника и 3-й как ноль, мы добавляем значения 4-го аргумента к 1-му аргументу

Тутанхамон
источник
Вы должны включить некоторые объяснения тем, кто не придерживается не удаляющей магии сплайсинга.
mickmackusa
0

если вы хотите объединить пустой массив с существующим новым значением. Вы должны инициализировать это сначала.

$products = array();
//just example
for($brand_id=1;$brand_id<=3;$brand_id++){
  array_merge($products,getByBrand($brand_id));
}
// it will create empty array
print_r($a);

//check if array of products is empty
for($brand_id=1;$brand_id<=3;$brand_id++){
  if(empty($products)){
    $products = getByBrand($brand_id);
  }else{
    array_merge($products,getByBrand($brand_id));
  }
}
// it will create array of products

Надеюсь, это поможет.

drosanda
источник
0

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

// Create an array of arrays
$chars = [];
for ($i = 0; $i < 15000; $i++) {
    $chars[] = array_fill(0, 10, 'a');
}

// test array_merge
$new = [];
$start = microtime(TRUE);
foreach ($chars as $splitArray) {
    $new = array_merge($new, $splitArray);
}
echo microtime(true) - $start; // => 14.61776 sec

// test foreach
$new = [];
$start = microtime(TRUE);
foreach ($chars as $splitArray) {
    foreach ($splitArray as $value) {
        $new[] = $value;
    }
}
echo microtime(true) - $start; // => 0.00900101 sec
// ==> 1600 times faster
E_d
источник
Этот ответ не приносит никакой новой информации на страницу. Сравнения производительности были опубликованы годами ранее.
mickmackusa
-4

Как насчет этого:

$appended = $a + $b;
Писквор покинул здание
источник
1
Он будет сравнивать ключи, как я уже сказал, и результат будет следующим: Array ([0] => a [1] => b)
Danil K
1
Вы уверены, что он будет сравнивать ключи? В документации (выделено мной): «Если входные массивы имеют одинаковые строковые ключи, то более позднее значение для этого ключа будет перезаписывать предыдущее. Если, однако, массивы содержат числовые ключи, более позднее значение не будет перезаписывать исходное значение, но будет добавлено. " Вы уверены, что ваши ключи на самом деле не '0' => 'a'... а не 0 => 'a'?
Писквор покинул здание
@Piskvor нет разницы между «0» и 0 для ключей.
Гордон
Гордон прав. Акцент делается на цифровые клавиши (в отличие от целочисленных клавиш ).
Netcoder
1
@ Гордон: Ах, ты прав - вот что я получаю, думая о двух вещах одновременно. php.net/manual/en/language.operators.array.php - документация дляarray + array
Писквор покинул здание