¿Hay una función para hacer una copia de una matriz PHP a otra?


530

¿Hay una función para hacer una copia de una matriz PHP a otra?

Me quemé varias veces tratando de copiar matrices PHP. Quiero copiar una matriz definida dentro de un objeto a un global fuera de él.


muy tarde, pero en mi entorno probé esto (y funcionó): function arrayCopy (array $ a) {return $ a; } $ a1 = array (); para ($ i = 0; $ i <3; $ i ++) {$ a1 ["clave- $ i"] = "valor # $ i"; } $ a1 ["key-sub-array"] = array (1, 2, 3, 4); $ a2 = $ a1; $ a3 = arrayCopy ($ a1); for ($ i = 0; $ i <3; $ i ++) {if (! is_array ($ a2 ["key- $ i"])) {$ a2 ["key- $ i"] = "valor cambiado # $ yo"; }} $ a2 ["key-sub-array"] = array ("subarreglo 1 cambiado", "subarreglo 2 cambiado"); var_dump ($ a1); var_dump ($ a2); var_dump ($ a3); El truco es, no pasar la matriz como referencia a la función ;-)
Sven

Respuestas:


927

En PHP, las matrices se asignan por copia, mientras que los objetos se asignan por referencia. Esto significa que:

$a = array();
$b = $a;
$b['foo'] = 42;
var_dump($a);

Rendirá:

array(0) {
}

Mientras:

$a = new StdClass();
$b = $a;
$b->foo = 42;
var_dump($a);

Rendimientos:

object(stdClass)#1 (1) {
  ["foo"]=>
  int(42)
}

Podría confundirse con complejidades como ArrayObject, que es un objeto que actúa exactamente como una matriz. Sin embargo, al ser un objeto, tiene semántica de referencia.

Editar: @AndrewLarsson plantea un punto en los comentarios a continuación. PHP tiene una característica especial llamada "referencias". Son algo similares a los punteros en lenguajes como C / C ++, pero no son lo mismo. Si su matriz contiene referencias, mientras la matriz se pasa por copia, las referencias aún se resolverán en el objetivo original. Por supuesto, ese suele ser el comportamiento deseado, pero pensé que valía la pena mencionarlo.


104
No respondiste la pregunta. Solo explicaste el problema. Lo cual, para el OP, es muy probablemente lo que estaba buscando. Sin embargo, para mí (y para otros también), viniendo aquí casi cuatro años después con un problema similar, todavía no tengo una buena manera de clonar una matriz sin modificar la matriz original (que también incluye punteros internos). Supongo que es hora de que haga mi propia pregunta.
Andrew Larsson

28
@AndrewLarsson Pero PHP lo hace de manera predeterminada: eso es lo esencial. Sin embargo, las referencias no se resuelven, por lo que si necesita eso, tendrá que atravesar recursivamente la matriz y construir una nueva. Del mismo modo, si la matriz fuente contiene objetos y desea clonarlos, deberá hacerlo manualmente. Tenga en cuenta también que las referencias en PHP no son lo mismo que los punteros en C. Sin saber nada sobre su caso, puedo sugerir que es extraño tener una serie de referencias en el primer caso, especialmente si no tiene intención de tratar ellos como referencias? ¿Cuál es el caso de uso?
troelskn

1
@troelskn Agregué una respuesta a esta pregunta con una solución a mi problema: stackoverflow.com/a/17729234/1134804
Andrew Larsson

3
Pero, ¿qué pasa cuando no es el comportamiento deseado? La pregunta pregunta cómo hacer una copia profunda . Obviamente no es deseado. Su respuesta no es mejor que es: $copy = $original;. Lo que no funciona si los elementos de la matriz son referencias.
doug65536

8
Como siempre phpnos presenta el resultado menos esperado , porque esta solución no siempre funciona . $a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];imprime array0mientras $a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];imprime array1. Aparentemente, algunas matrices se copian por referencia.
Tino

186

PHP copiará la matriz por defecto. Las referencias en PHP deben ser explícitas.

$a = array(1,2);
$b = $a; // $b will be a different array
$c = &$a; // $c will be a reference to $a

Usar la referencia puede ser importante si la matriz es enorme. No estoy seguro, pero supongo que debería conducir a un menor consumo de memoria y un mejor rendimiento (no es necesario copiar toda la matriz en la memoria).
Robsch

11
@robsch: en el nivel de la lógica del programa, la matriz se copia. Pero en la memoria, en realidad no se copiará hasta que se modifique, porque PHP usa semántica de copia en escritura para todos los tipos. stackoverflow.com/questions/11074970/…
Jessica Knight

@CoreyKnight Es bueno saberlo. Gracias por esto.
robsch

44
tenga en cuenta que esto no es cierto para las matrices anidadas, son referencias y, por lo tanto, termina con un desastre roto
MightyPork

45

Si tiene una matriz que contiene objetos, debe hacer una copia de esa matriz sin tocar su puntero interno, y necesita clonar todos los objetos (para que no esté modificando los originales cuando realice cambios en la copia) matriz), use esto.

El truco para no tocar el puntero interno de la matriz es asegurarse de que está trabajando con una copia de la matriz y no con la matriz original (o una referencia a ella), por lo que usar un parámetro de función hará el trabajo (por lo tanto, Esta es una función que toma una matriz).

Tenga en cuenta que aún necesitará implementar __clone () en sus objetos si desea que sus propiedades también se clonen.

Esta función funciona para cualquier tipo de matriz (incluido el tipo mixto).

function array_clone($array) {
    return array_map(function($element) {
        return ((is_array($element))
            ? array_clone($element)
            : ((is_object($element))
                ? clone $element
                : $element
            )
        );
    }, $array);
}

1
Tenga en cuenta que este es un caso un poco especial. Además, tenga en cuenta que esto solo clonará las referencias de primer nivel. Si tiene una matriz profunda, no clonará los nodos más profundos, si son referencias. Puede que no sea un problema en tu caso, pero solo tenlo en cuenta.
troelskn

44
@troelskn Lo arreglé agregando algo de recursión. Esta función ahora funcionaría en cualquier tipo de matriz, incluidos los tipos mixtos. También funciona igual de bien para matrices simples, por lo que ya no está localizado. Básicamente es una máquina universal de clonación de matrices. Aún necesitaría definir la función __clone () en sus objetos si son profundos, pero eso está más allá del "alcance" de esta función (perdón por el mal juego de palabras).
Andrew Larsson

2
Creo firmemente que esta es la respuesta real a esta pregunta, la única forma en que he visto copiar en profundidad una matriz que contiene objetos.
Patrick

No itera las propiedades de los objetos que pueden tener otras matrices y objetos referenciados.
ya.teck

66
Este uso de __FUNCTION__es brillante.
zessx

29

Cuando tu lo hagas

$array_x = $array_y;

PHP copia la matriz, así que no estoy seguro de cómo te habrías quemado. Para su caso

global $foo;
$foo = $obj->bar;

Debería funcionar bien.

Para quemarse, creo que tendrías que haber estado usando referencias o esperando que los objetos dentro de las matrices sean clonados.


12
+1 para esto: "o esperando que se
clonen


18

simple y hace una copia profunda rompiendo todos los enlaces

$new=unserialize(serialize($old));

44
En general, funciona bien, sin embargo, en algunos casos puede arrojar una excepción porque no todas las variables son serializables (por ejemplo, cierres y conexiones de bases de datos).
ya.teck

Otra cosa a tener en cuenta es que las referencias a objetos se pueden restaurar si una clase implementa el método mágico __wakeup.
ya.teck

Gracias, finalmente algo que realmente funciona, no las otras respuestas de Bollock que tienen muchos votos positivos, seguramente no trataron con una matriz de objetos como se especifica en la pregunta donde podría cambiar el número de elementos en la matriz, pero definitivamente no las referencias a la objetos dentro de ellos
FentomX1

12

Me gusta array_replace(o array_replace_recursive)

$cloned = array_replace([], $YOUR_ARRAY);

Funciona como Object.assigndesde JavaScript.

$original = [ 'foo' => 'bar', 'fiz' => 'baz' ];

$cloned = array_replace([], $original);
$clonedWithReassignment = array_replace([], $original, ['foo' => 'changed']);
$clonedWithNewValues = array_replace([], $original, ['add' => 'new']);

$original['new'] = 'val';

resultará en

// original: 
{"foo":"bar","fiz":"baz","new":"val"}
// cloned:   
{"foo":"bar","fiz":"baz"}
// cloned with reassignment:
{"foo":"changed","fiz":"baz"}
// cloned with new values:
{"foo":"bar","fiz":"baz","add":"new"}

1
¿Qué pasa array_slice($arr, 0)o cuando no te importan las llaves array_values($arr)? Estoy pensando que podrían ser más rápidos que buscar en una matriz. Además, en javascript, es bastante popular usarlo Array.slice()para clonar matrices.
Christian

En JS tenemos Object para pares clave-valor y Array . PHP no hace esta diferencia. Para matrices PHP con índices numerados, array_slicey todos los demás métodos mencionados aquí funcionan muy bien. Pero si desea fusionar varios pares clave-valor (como también es posible con JS-Objects vía Object.assigno la sintaxis spread ), array_replacepuede ser más útil.
Putzi San

@ Christian gracias por la sugerencia de array_values()que funcionó perfectamente para mi caso de uso.
Bigsee

11

Si solo tiene tipos básicos en su matriz, puede hacer esto:

$copy = json_decode( json_encode($array), true);

No necesitará actualizar las referencias manualmente
. Sé que no funcionará para todos, pero funcionó para mí.


44
+1 esto es algo realmente malo, pero es técnicamente correcto e inteligente. Si veía esto en el código, me enfrentaría a la palma, pero no puedo evitar que me guste.
Reactgular

4

Como esto no estaba cubierto en ninguna de las respuestas y ahora está disponible en PHP 5.3 (se suponía que la publicación original estaba usando 5.2).

Para mantener una estructura de matriz y cambiar sus valores, prefiero usar array_replaceo array_replace_recursivedependiendo de mi caso de uso.

http://php.net/manual/en/function.array-replace.php

Aquí hay un ejemplo usando array_replacey array_replace_recursivedemostrando que es capaz de mantener el orden indexado y capaz de eliminar una referencia.

http://ideone.com/SzlBUZ

El siguiente código está escrito utilizando la sintaxis de matriz corta disponible desde PHP 5.4 que reemplaza array()con []. http://php.net/manual/en/language.types.array.php

Funciona en matrices indexadas indexadas o indexadas

$o1 = new stdClass;
$a = 'd';
//This is the base array or the initial structure
$o1->ar1 = ['a', 'b', ['ca', 'cb']];
$o1->ar1[3] = & $a; //set 3rd offset to reference $a

//direct copy (not passed by reference)
$o1->ar2 = $o1->ar1; //alternatively array_replace($o1->ar1, []);
$o1->ar1[0] = 'z'; //set offset 0 of ar1 = z do not change ar2
$o1->ar1[3] = 'e'; //$a = e (changes value of 3rd offset to e in ar1 and ar2)

//copy and remove reference to 3rd offset of ar1 and change 2nd offset to a new array
$o1->ar3 = array_replace($o1->ar1, [2 => ['aa'], 3 => 'd']);

//maintain original array of the 2nd offset in ar1 and change the value at offset 0
//also remove reference of the 2nd offset
//note: offset 3 and 2 are transposed
$o1->ar4 = array_replace_recursive($o1->ar1, [3 => 'f', 2 => ['bb']]);

var_dump($o1);

Salida:

["ar1"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "ca"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    &string(1) "e"
  }
  ["ar2"]=>
  array(4) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "ca"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    &string(1) "e"
  }
  ["ar3"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(1) {
      [0]=>
      string(2) "aa"
    }
    [3]=>
    string(1) "d"
  }
  ["ar4"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "bb"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    string(1) "f"
  }


2

Esta es la forma en que estoy copiando mis matrices en Php:

function equal_array($arr){
  $ArrayObject = new ArrayObject($arr);
  return $ArrayObject->getArrayCopy();  
}

$test = array("aa","bb",3);
$test2 = equal_array($test);
print_r($test2);

Esto produce:

Array
(
[0] => aa
[1] => bb
[2] => 3
)

2
¿Por qué no solo decir $test2 = $test;? ¿Qué problema está ArrayObjectresolviendo aquí?
Nate

1
<?php
function arrayCopy( array $array ) {
        $result = array();
        foreach( $array as $key => $val ) {
            if( is_array( $val ) ) {
                $result[$key] = arrayCopy( $val );
            } elseif ( is_object( $val ) ) {
                $result[$key] = clone $val;
            } else {
                $result[$key] = $val;
            }
        }
        return $result;
}
?>

1

La forma más segura y barata que encontré es:

<?php 
$b = array_values($a);

Esto también tiene el beneficio de reindexar la matriz.

Esto no funcionará como se esperaba en una matriz asociativa (hash), pero tampoco la mayoría de las respuestas anteriores.


1

Crea una copia del objeto Array

<?php
// Array of available fruits
$fruits = array("lemons" => 1, "oranges" => 4, "bananas" => 5, "apples" => 10);

$fruitsArrayObject = new ArrayObject($fruits);
$fruitsArrayObject['pears'] = 4;

// create a copy of the array
$copy = $fruitsArrayObject->getArrayCopy();
print_r($copy);

?>

de https://www.php.net/manual/en/arrayobject.getarraycopy.php


0

Define esto:

$copy = create_function('$a', 'return $a;');

Copie $ _ARRAY en $ _ARRAY2:

$_ARRAY2 = array_map($copy, $_ARRAY);

0

En php array, solo debe asignarlos a otra variable para obtener una copia de esa matriz. Pero primero debe asegurarse de su tipo, ya sea array o arrayObject o stdObject.

Para la matriz php simple:

$a = array(
'data' => 10
);

$b = $a;

var_dump($b);

output:

array:1 [
  "data" => 10
]

0
private function cloneObject($mixed)
{
    switch (true) {
        case is_object($mixed):
            return clone $mixed;
        case is_array($mixed):
            return array_map(array($this, __FUNCTION__), $mixed);
        default:
            return $mixed;
    }
}

0

$arr_one_copy = array_combine(array_keys($arr_one), $arr_one);

Solo para publicar una solución más;)


-1
foreach($a as $key => $val) $b[$key] = $val ;

Conserva tanto la clave como los valores. La matriz 'a' es una copia exacta de la matriz 'b'

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.