Czy istnieje funkcja umożliwiająca skopiowanie tablicy PHP do innej?


529

Czy istnieje funkcja umożliwiająca skopiowanie tablicy PHP do innej?

Zostałem spalony kilka razy, próbując skopiować tablice PHP. Chcę skopiować tablicę zdefiniowaną wewnątrz obiektu na globalny poza nim.


naprawdę późno, ale w moim środowisku przetestowałem to (i zadziałało): function arrayCopy (tablica $ a) {return $ a; } $ a1 = array (); for ($ i = 0; $ i <3; $ i ++) {$ a1 ["key- $ i"] = "wartość # $ i"; } $ a1 [„key-sub-array”] = tablica (1, 2, 3, 4); $ a2 = $ a1; $ a3 = arrayCopy ($ a1); for ($ i = 0; $ i <3; $ i ++) {if (! is_array ($ a2 ["key- $ i"])) {$ a2 ["key-$ i"] = "zmieniono wartość # $ ja"; }} $ a2 [„key-sub-array”] = tablica („zmieniona podgrupa 1”, „zmieniona podgrupa 2”); var_dump ($ a1); var_dump ($ a2); var_dump ($ a3); Sztuka polega na tym, aby nie przekazywać tablicy jako odwołania do funkcji ;-)
Sven

Odpowiedzi:


926

W PHP tablice są przypisywane przez kopiowanie, podczas gdy obiekty są przypisywane przez odniesienie. To znaczy że:

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

Przyniesie:

array(0) {
}

Natomiast:

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

Wydajność:

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

Możesz się pomylić z zawiłościami takimi jak ArrayObject, który jest obiektem, który działa dokładnie jak tablica. Będąc obiektem ma jednak semantykę odniesienia.

Edycja: @AndrewLarsson podnosi punkt w komentarzach poniżej. PHP ma specjalną funkcję zwaną „referencjami”. Są one nieco podobne do wskaźników w językach takich jak C / C ++, ale nie do końca takie same. Jeśli tablica zawiera referencje, to podczas gdy sama tablica jest przekazywana przez kopię, referencje nadal będą rozstrzygane w pierwotnym celu. To zwykle pożądane zachowanie, ale pomyślałem, że warto o tym wspomnieć.


104
Nie odpowiedziałeś na pytanie. Wyjaśniłeś tylko problem. Którego dla OP najprawdopodobniej szukał. Jednak dla mnie (i innych), przybywającego tutaj prawie cztery lata później z podobnym problemem, nadal nie mam dobrego sposobu klonowania tablicy bez modyfikacji oryginalnej tablicy (która obejmuje również wewnętrzne wskaźniki). Przypuszczam, że nadszedł czas, aby zadać własne pytanie.
Andrew Larsson,

28
@AndrewLarsson Ale PHP robi to domyślnie - to sedno tego. Referencje nie są jednak rozwiązywane, więc jeśli będziesz tego potrzebować, będziesz musiał rekurencyjnie przejść przez tablicę i zbudować nową - Podobnie, jeśli tablica źródłowa zawiera obiekty i chcesz, aby zostały sklonowane, musisz to zrobić ręcznie. Pamiętaj również, że odniesienia w PHP nie są takie same jak wskaźniki w C. Nie wiedząc nic o twojej sprawie, sugeruję, że to dziwne, że macie szereg referencji w pierwszym przypadku, szczególnie jeśli nie zamierzasz leczyć je jako odniesienia? Jaki jest przypadek użycia?
troelskn

1
@troelskn Dodałem odpowiedź na to pytanie z rozwiązaniem mojego problemu: stackoverflow.com/a/17729234/1134804
Andrew Larsson

3
Ale co, gdy nie jest pożądane zachowanie? Pytanie dotyczy sposobu wykonania głębokiej kopii. To oczywiście nie jest pożądane. Twoja odpowiedź nie jest lepsza niż to: $copy = $original;. Co nie działa, jeśli elementy tablicy są odwołaniami.
doug65536

8
Jak zawsze phpprzedstawia nam najmniej oczekiwany wynik , ponieważ to rozwiązanie nie zawsze działa . $a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];drukuje array0podczas $a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];drukowania array1. Najwyraźniej niektóre tablice są kopiowane przez odniesienie.
Tino

186

PHP domyślnie skopiuje tablicę. Odnośniki w PHP muszą być jawne.

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

Użycie odniesienia może być ważne, jeśli tablica jest ogromna. Nie jestem pewien, ale zakładam, że powinno to prowadzić do mniejszego zużycia pamięci i lepszej wydajności (nie trzeba kopiować całej tablicy do pamięci).
robsch

11
@robsch - na poziomie logiki programu tablica jest kopiowana. Ale w pamięci nie zostanie skopiowane, dopóki nie zostanie zmodyfikowane - ponieważ PHP używa semantyki kopiowania przy zapisie dla wszystkich typów. stackoverflow.com/questions/11074970/…
Jessica Knight

@CoreyKnight Dobrze wiedzieć. Dziękuję Ci za to.
robsch

4
zwróć uwagę, że nie jest to prawdą w przypadku tablic zagnieżdżonych, są to odniesienia, więc
kończysz

45

Jeśli masz tablicę zawierającą obiekty, musisz wykonać kopię tej tablicy bez dotykania jej wewnętrznego wskaźnika i potrzebujesz wszystkich obiektów do klonowania (aby nie modyfikować oryginałów podczas wprowadzania zmian w kopiowanym tablica), użyj tego.

Sztuką nie dotykania wewnętrznego wskaźnika tablicy jest upewnienie się, że pracujesz z kopią tablicy, a nie z oryginalną tablicą (lub odniesieniem do niej), więc użycie parametru funkcji wykona zadanie (a zatem jest to funkcja, która przyjmuje tablicę).

Pamiętaj, że nadal będziesz musiał zaimplementować __clone () na swoich obiektach, jeśli chcesz, aby ich właściwości również były klonowane.

Ta funkcja działa dla dowolnego typu tablicy (w tym typu mieszanego).

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

1
Pamiętaj, że jest to szczególny przypadek. Pamiętaj też, że spowoduje to klonowanie tylko referencji pierwszego poziomu. Jeśli masz głęboką tablicę, nie uzyskasz klonowania głębszych węzłów, jeśli są one referencjami. W twoim przypadku może to nie stanowić problemu, ale pamiętaj o tym.
troelskn

4
@troelskn Naprawiłem to, dodając rekurencję. Ta funkcja będzie teraz działać na dowolnym typie tablicy, w tym na typach mieszanych. Działa równie dobrze dla prostych tablic, więc nie jest już lokalizowany. Jest to w zasadzie uniwersalna maszyna do klonowania macierzy. Nadal będziesz musiał zdefiniować funkcję __clone () w swoich obiektach, jeśli są one głębokie, ale to wykracza poza „zasięg” tej funkcji (przepraszam za zły kalambur).
Andrew Larsson,

2
Mocno wierzę, że to jest prawdziwa odpowiedź na to pytanie. Jedyny sposób, w jaki widziałem głębokie kopiowanie tablicy zawierającej obiekty.
Patrick,

Nie iteruje właściwości obiektów, które mogą mieć inne tablice i obiekty odniesienia.
ya.teck

6
To zastosowanie __FUNCTION__jest genialne.
zessx

29

Kiedy to zrobisz

$array_x = $array_y;

PHP kopiuje tablicę, więc nie jestem pewien, jak zostałbyś spalony. W twoim przypadku

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

powinien działać dobrze.

Aby się poparzyć, pomyślałbym, że musiałbyś albo użyć referencji, albo oczekiwać, że obiekty wewnątrz tablic zostaną sklonowane.


12
+1 za to: „lub
oczekuję,


18

proste i sprawia, że ​​głębokie kopiowanie przerywa wszystkie linki

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

4
Zasadniczo działa dobrze, jednak w niektórych przypadkach może zgłosić wyjątek, ponieważ nie wszystkie zmienne są serializowane (na przykład zamknięcia i połączenia z bazą danych).
ya.teck

Inną rzeczą wartą uwagi jest to, że odwołania do obiektów można przywrócić, jeśli klasa implementuje magiczną metodę __wakeup.
ya.teck

Dzięki, w końcu coś, co naprawdę działa, a nie inne bollockowe odpowiedzi, które mają wiele pozytywnych opinii, z pewnością nie zajmowały się tablicą obiektów, jak określono w pytaniu, w którym liczba elementów w tablicy może się zmienić, ale zdecydowanie nie odniesienia do przedmioty w nich zawarte
FentomX1

12

Lubię array_replace(lub array_replace_recursive).

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

Działa jak Object.assignz 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';

spowoduje

// 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
A array_slice($arr, 0)co z kluczami, kiedy nie zależy ci na nich array_values($arr)? Myślę, że mogą być szybsze niż wyszukiwanie w tablicy. Ponadto w javascript jest dość popularne Array.slice()do klonowania tablic.
Christian

W JS mamy Object dla par klucz-wartość i Array . PHP nie robi tej różnicy. W przypadku tablic PHP z numerowanymi indeksami array_slicewszystkie pozostałe metody tutaj wymienione działają bardzo dobrze. Ale jeśli chcesz scalić kilka par klucz-wartość (jak jest to również możliwe w przypadku obiektów JS za pośrednictwem Object.assignlub składni rozproszonej ), array_replacemoże być bardziej przydatne.
Putzi San

@Christian dziękuję za sugestię, array_values()która działała idealnie w moim przypadku użycia.
bigsee

11

Jeśli masz w tablicy tylko podstawowe typy, możesz to zrobić:

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

Nie będziesz musiał ręcznie aktualizować referencji.
Wiem, że to nie będzie działać dla wszystkich, ale zadziałało dla mnie


4
+1 to jest naprawdę zła rzecz do zrobienia, ale jest technicznie poprawna i sprytna. Gdybym zobaczył to w kodzie, stawiłbym czoła dłoni, ale nie mogę nic na to poradzić.
Reactgular,

4

Ponieważ nie zostało to uwzględnione w żadnej z odpowiedzi i jest teraz dostępne w PHP 5.3 (zakładając, że Original Post używa 5.2).

W celu utrzymania struktury tablicy i zmiany jej wartości wolę używać array_replacelub w array_replace_recursivezależności od mojego przypadku użycia.

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

Oto przykład użycia array_replacei array_replace_recursivewykazania, że ​​jest w stanie utrzymać indeksowaną kolejność i może usunąć odwołanie.

http://ideone.com/SzlBUZ

Poniższy kod został napisany przy użyciu krótkiej składni tablicowej dostępnej od PHP 5.4, która zastępuje array()się []. http://php.net/manual/en/language.types.array.php

Działa zarówno na tablicach indeksowanych offsetowo, jak i indeksowanych nazwach

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

Wynik:

["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

W ten sposób kopiuję moje tablice w Php:

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

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

To daje:

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

2
Dlaczego po prostu nie powiesz $test2 = $test;? Jaki problem ArrayObjecttutaj rozwiązujesz?
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

Najbezpieczniejszy i najtańszy sposób jaki znalazłem to:

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

Ma to również tę zaletę, że reindeksuje tablicę.

To nie będzie działać zgodnie z oczekiwaniami dla tablicy asocjacyjnej (skrótu), ale nie będzie w przypadku większości poprzednich odpowiedzi.


1

Tworzy kopię obiektu ArrayObject

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

?>

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


0

Zdefiniuj to:

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

Skopiuj $ _ARRAY do $ _ARRAY2:

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

0

W tablicy php wystarczy przypisać je do innej zmiennej, aby uzyskać kopię tej tablicy. Ale najpierw musisz upewnić się co do jego typu, niezależnie od tego, czy jest to tablica, czy obiekt tablicowy, czy obiekt stdObject.

Dla prostej tablicy php:

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

Wystarczy opublikować jeszcze jedno rozwiązanie;)


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

Zachowuje zarówno klucz, jak i wartości. Tablica „a” jest dokładną kopią tablicy „b”

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.