PHP agrega una matriz a otra (no array_push o +)

278

¿Cómo agregar una matriz a otra sin comparar sus claves?

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

Al final debería ser: Array( [0]=>a [1]=>b [2]=>c [3]=>d ) si uso algo como []o array_push, causará uno de estos resultados:

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

Simplemente debería ser algo, hacer esto, pero de una manera más elegante:

foreach ( $b AS $var )
    $a[] = $var;
Danil K
fuente
16
array_merge ($a, $b)debe hacer exactamente lo que quiere, al menos con PHP 5+.
tloach
1
(relacionado) + Operador para Array en PHP
Gordon
66
ninguna de las salidas que publicó proviene de array_merge();la salida de array_merge();debe ser exactamente lo que necesita:print_r(array_merge($a,$b)); // outputs => Array ( [0] => a [1] => b [2] => c [3] => d )
acm
2
Estoy totalmente en desacuerdo con el término "agregar". Anexar realmente significa que los elementos de una matriz se convierten en elementos de otra matriz (destino) que ya puede tener algunos elementos, por lo tanto, cambia la matriz de destino. Fusionar asigna una nueva matriz y COPIA elementos de ambas matrices, mientras que agregar realmente significa reutilizar los elementos de la matriz de destino sin asignación de memoria adicional.
tishma

Respuestas:

425

array_merge es la forma elegante:

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

Haciendo algo como:

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

No funcionará, porque el +operador no los fusiona realmente. Si $atienen las mismas claves que $b, no hará nada.

netcoder
fuente
16
Solo tenga cuidado si sus claves no son números sino cadenas, desde el documento: si las matrices de entrada tienen las mismas claves de cadena, entonces el valor posterior para esa clave sobrescribirá la anterior
Dusan Plavak
o use el operador moderno de splat como @bstoney answer stackoverflow.com/a/37065301/962634
basil
76

Otra forma de hacer esto en PHP 5.6+ sería usar el ...token

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

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

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

Esto también funcionará con cualquier 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');

Sin embargo, una advertencia :

  • en versiones de PHP anteriores a 7.3, esto causará un error fatal si $bes una matriz vacía o no es transitable, por ejemplo, no es una matriz
  • en PHP 7.3 se generará una advertencia si $bno es transitable
bstoney
fuente
¿Qué término se usa para tal sintaxis? (Por ejemplo, en JS se llama operador de propagación) ¿O puede proporcionar un enlace a documentos?
albahaca
3
@basil encontrará ...comúnmente conocido como splat operatoren php.
mickmackusa
La respuesta más útil cuando se busca una forma simple de agregar una matriz a sí misma sin anular ningún elemento anterior.
Daniel Böttner
1
array_pushacepta un único argumento desde php 7.3, que evita errores con matrices vacías.
vctls
En realidad, esta es la forma más elegante y eficiente. gracias
Hassan Ali Salem
33

¿Por qué no usar

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

¿Por qué no quieres usar este, el método correcto incorporado?

Mark Baker
fuente
¿Dónde dice OP que "no quiere usar" array_merge () ...?
KittenCodings 01 de
3
@ KittenCodings - Lea el "historial de edición" de la pregunta ... la pregunta original tenía derecho PHP append one array to another (not array_merge or array_push)... posteriormente modificada PHP append one array to another (not array_merge or +)antes de cambiar a su título actual
Mark Baker
2
@ MarkBaker ¡Guau! ¡No sabía que SO tiene un historial de edición! Lo siento, y gracias, esto cambia mucho y de alguna manera evita que los moderadores pongan palabras en la boca de las personas, anteriormente sentí que algunas preguntas fueron borradas y sus comentarios invalidados por el contenido eliminado / editado, aunque imagino que la mayoría de la gente probablemente no lea el historial de edición, estoy seguro que como diablos a partir de ahora
KittenCodings
21

Es una publicación bastante antigua, pero quiero agregar algo sobre agregar una matriz a otra:

Si

  • una o ambas matrices tienen claves asociativas
  • las claves de ambas matrices no importan

puedes usar funciones de matriz como esta:

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

array_merge no combina claves numéricas, por lo que agrega todos los valores de $ appendArray. Si bien utiliza funciones php nativas en lugar de un bucle foreach, debería ser más rápido en matrices con muchos elementos.

Adición 2019-12-13: desde PHP 7.4, existe la posibilidad de agregar o anteponer matrices de la manera del Operador de distribución de matriz:

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

Como antes, las claves pueden ser un problema con esta nueva característica:

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

"Error grave: error no detectado: no se puede descomprimir la matriz con claves de cadena"

    $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
fuente
1
Esto también debería tener la ventaja de dejar intactos los arreglos de entrada.
Jon Surrell
1
Sí, es más seguro por si acaso extraer los valores de matriz para que no se fusionen en las mismas claves.
Gabriel Rodriguez
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));
?>

Salida:

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
)

Código fuente de referencia

Hassan Amir Khan
fuente
12

Para una matriz grande, es mejor concatenar sin array_merge, para evitar una copia de memoria.

$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
Snark
fuente
Funciona de maravilla, para mí este enfoque fue 50 veces más rápido.
luttkens
9

Siguiendo las respuestas de bstoney y Snark, realicé algunas pruebas sobre los distintos métodos:

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

Que produce:

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

ORIGINAL: Creo que a partir de PHP 7, el método 3 es una alternativa significativamente mejor debido a la forma en que ahora actúan los bucles foreach , que es hacer una copia de la matriz que se repite.

Si bien el método 3 no es estrictamente una respuesta a los criterios de 'not array_push' en la pregunta, es una línea y el rendimiento más alto en todos los aspectos, creo que la pregunta se hizo antes de ... la sintaxis era una opción.

ACTUALIZACIÓN 25/03/2020: He actualizado la prueba que fue defectuosa ya que las variables no se restablecieron. ¡Curiosamente (o confusamente) los resultados ahora muestran que la prueba 1 es la más rápida, donde fue la más lenta, habiendo pasado de 0.008392 a 0.002717! Esto solo puede deberse a actualizaciones de PHP, ya que esto no se habría visto afectado por la falla de prueba.

Entonces, la saga continúa, ¡comenzaré a usar array_merge a partir de ahora!

Jamie Robinson
fuente
2
No reinicia array1 antes de cada prueba, por lo que cada prueba tiene 50,000 elementos más que la anterior.
Dakusan
Increíble después de tantos años, eres la primera persona que me recoge esto, gracias, haré una nueva prueba en breve :)
Jamie Robinson
5

A partir de PHP 7.4 se puede utilizar el ... operador . Esto también se conoce como el operador splat en otros idiomas, incluido Ruby.

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

Salida

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

El operador Splat debería tener un mejor rendimiento que array_merge . Esto no solo se debe a que el operador splat es una estructura de lenguaje, mientras que array_merge es una función, sino también a que la optimización del tiempo de compilación puede ser eficaz para matrices constantes.

Además, podemos usar la sintaxis del operador splat en cualquier lugar de la matriz, ya que se pueden agregar elementos normales antes o después del operador splat.

$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$arr3 = [...$arr1, ...$arr2];
$arr4 = [...$arr1, ...$arr3, 7, 8, 9];
dtar
fuente
3

Antes de PHP7 puedes usar:

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

array_splice()opera con referencia a la matriz (1er argumento) y coloca los valores de la matriz (4to argumento) en lugar de la lista de valores comenzados desde el 2do argumento y el número del 3er argumento. Cuando establecemos el segundo argumento como final de la matriz fuente y el tercero como cero, agregamos los valores del cuarto argumento al primer argumento

Tutankamón
fuente
Debe incluir alguna explicación para aquellos que no siguen la magia de empalme que no se elimina.
mickmackusa
0

si desea fusionar una matriz vacía con un nuevo valor existente. Debes inicializarlo primero.

$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

Espero que sea de ayuda.

drosanda
fuente
0

foreach loop es más rápido que array_merge para agregar valores a una matriz existente, así que elija el bucle en su lugar si desea agregar una matriz al final de otra.

// 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
fuente
Esta respuesta no trae información nueva a la página. Las comparaciones de rendimiento se publicaron años antes.
mickmackusa
-4

Qué tal esto:

$appended = $a + $b;
Piskvor salió del edificio
fuente
1
Comparará claves, como dije, y resultará con lo siguiente: Matriz ([0] => a [1] => b)
Danil K
1
¿Estás seguro de que comparará las claves? Dice la documentación (énfasis mío): "Si las matrices de entrada tienen las mismas claves de cadena, entonces el valor posterior para esa clave sobrescribirá la anterior. Sin embargo, si las matrices contienen claves numéricas, el valor posterior no sobrescribirá el original valor, pero se agregará ". ¿Estás seguro de que tus llaves no son realmente '0' => 'a'... en lugar de 0 => 'a'?
Piskvor salió del edificio el
@Piskvor no hay diferencia entre '0' y 0 para las teclas.
Gordon
Gordon tiene razón. El énfasis está en las teclas numéricas (a diferencia de las teclas enteras ).
netcoder
1
@ Gordon: Ah, tienes razón, eso es lo que obtengo por pensar en dos cosas a la vez. php.net/manual/en/language.operators.array.php es documentación paraarray + array
Piskvor dejó el edificio