Có một chức năng để tạo một bản sao của một mảng PHP sang một mảng khác không?


529

Có một chức năng để tạo một bản sao của một mảng PHP sang một mảng khác không?

Tôi đã bị đốt cháy một vài lần khi cố gắng sao chép mảng PHP. Tôi muốn sao chép một mảng được xác định bên trong một đối tượng ra toàn cầu bên ngoài nó.


thực sự rất muộn, nhưng trong Môi trường của tôi, tôi đã thử nghiệm điều này (và nó đã hoạt động): function ArrayCopy (mảng $ a) {return $ a; } $ a1 = mảng (); for ($ i = 0; $ i <3; $ i ++) {$ a1 ["key- $ i"] = "value # $ i"; } $ a1 ["key-sub-Array"] = mảng (1, 2, 3, 4); $ a2 = $ a1; $ a3 = mảngCopy ($ a1); for ($ i = 0; $ i <3; $ i ++) {if (! is_array ($ a2 ["key- $ i"])) {$ a2 ["key- $ i"] = "giá trị thay đổi # $ Tôi"; }} $ a2 ["key-sub-Array"] = mảng ("thay đổi mảng con 1", "thay đổi mảng con 2"); var_dump ($ a1); var_dump ($ a2); var_dump ($ a3); Bí quyết là, không chuyển mảng làm tham chiếu vào hàm ;-)
Sven

Câu trả lời:


926

Trong mảng PHP được gán bởi bản sao, trong khi các đối tượng được gán bởi tham chiếu. Điều này có nghĩa rằng:

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

Sẽ mang lại:

array(0) {
}

Trong khi:

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

Sản lượng:

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

Bạn có thể bị lẫn lộn bởi những điều phức tạp như ArrayObject, đó là một đối tượng hoạt động chính xác như một mảng. Là một đối tượng, tuy nhiên, nó có ngữ nghĩa tham khảo.

Chỉnh sửa: @AndrewLarsson nêu lên một điểm trong các bình luận bên dưới. PHP có một tính năng đặc biệt gọi là "tài liệu tham khảo". Chúng có phần giống với con trỏ trong các ngôn ngữ như C / C ++, nhưng không hoàn toàn giống nhau. Nếu mảng của bạn chứa các tham chiếu, thì trong khi bản thân mảng được truyền bằng bản sao, các tham chiếu vẫn sẽ phân giải đến đích ban đầu. Tất nhiên đó thường là hành vi mong muốn, nhưng tôi nghĩ nó đáng được đề cập.


104
Bạn đã không trả lời câu hỏi. Bạn chỉ giải thích vấn đề. Mà, đối với OP, rất có thể là những gì anh đang tìm kiếm. Tuy nhiên, đối với tôi (và những người khác nữa), đến đây gần bốn năm sau với một vấn đề tương tự, tôi vẫn không có cách nào tốt để sao chép một mảng mà không sửa đổi mảng ban đầu (bao gồm cả con trỏ nội bộ). Tôi cho rằng đã đến lúc tôi phải tự đặt câu hỏi.
Andrew Larsson

28
@AndrewLarsson Nhưng mặc định PHP làm điều đó - Đó là ý chính của nó. Tuy nhiên, các tham chiếu không được giải quyết, vì vậy nếu bạn cần điều đó, bạn sẽ phải truy xuất đệ quy mảng và xây dựng một mảng mới - Tương tự như vậy, nếu mảng nguồn chứa các đối tượng và bạn muốn chúng được nhân bản, bạn sẽ phải thực hiện thủ công. Cũng nên nhớ rằng các tài liệu tham khảo trong PHP không giống như con trỏ trong C. Không biết gì về trường hợp của bạn, tôi có thể đề nghị rằng thật lạ khi có một loạt các tài liệu tham khảo trong trường hợp đầu tiên, đặc biệt là nếu bạn không có ý định điều trị chúng làm tài liệu tham khảo? Trường hợp sử dụng là gì?
troelskn

1
@troelskn Tôi đã thêm một câu trả lời cho câu hỏi này bằng một giải pháp cho vấn đề của mình: stackoverflow.com/a/17729234/1134804
Andrew Larsson

3
Nhưng những gì về khi nó không phải là hành vi mong muốn? Câu hỏi hỏi làm thế nào để tạo một bản sao sâu . Nó rõ ràng là không mong muốn. Câu trả lời của bạn không tốt hơn : $copy = $original;. Mà không hoạt động nếu các phần tử mảng là tài liệu tham khảo.
doug65536

8
Như luôn luôn phptrình bày cho chúng tôi kết quả ít mong đợi nhất , bởi vì giải pháp này không phải lúc nào cũng hoạt động . $a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];in array0trong khi $a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];in array1. Rõ ràng một số mảng được sao chép bằng cách tham khảo.
Tino

186

PHP sẽ sao chép mảng theo mặc định. Tài liệu tham khảo trong PHP phải rõ ràng.

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

Để sử dụng tham chiếu có thể quan trọng nếu mảng rất lớn. Tôi không chắc chắn nhưng tôi cho rằng nó sẽ dẫn đến tiêu thụ bộ nhớ ít hơn và hiệu suất tốt hơn (không cần phải sao chép toàn bộ mảng trong bộ nhớ).
robsch

11
@robsch - ở cấp độ logic của chương trình, mảng được sao chép. Nhưng trong bộ nhớ, nó thực sự sẽ không được sao chép cho đến khi nó được sửa đổi - bởi vì PHP sử dụng ngữ nghĩa sao chép trên ghi cho tất cả các loại. stackoverflow.com/questions/11074970/ Mạnh
Jessica Knight

@CoreyKnight Tốt để biết. Cảm ơn vì điều này.
robsch

4
lưu ý rằng điều này không đúng với các mảng lồng nhau, chúng là các tham chiếu và do đó bạn kết thúc bằng một mớ hỗn độn
MightyPork

45

Nếu bạn có một mảng chứa các đối tượng, bạn cần tạo một bản sao của mảng đó mà không chạm vào con trỏ bên trong của nó và bạn cần tất cả các đối tượng được sao chép (để bạn không sửa đổi bản gốc khi bạn thay đổi bản sao mảng), sử dụng này.

Mẹo để không chạm vào con trỏ bên trong của mảng là đảm bảo bạn đang làm việc với một bản sao của mảng chứ không phải mảng ban đầu (hoặc tham chiếu đến nó), do đó, sử dụng tham số hàm sẽ hoàn thành công việc (do đó, đây là một hàm có trong một mảng).

Lưu ý rằng bạn vẫn sẽ cần triển khai __clone () trên các đối tượng của mình nếu bạn muốn các thuộc tính của chúng cũng được sao chép.

Hàm này hoạt động cho bất kỳ loại mảng nào (bao gồm cả loại hỗn hợp).

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

1
Hãy nhớ rằng đây là một trường hợp đặc biệt. Ngoài ra, lưu ý rằng điều này sẽ chỉ sao chép các tham chiếu cấp đầu tiên. Nếu bạn có một mảng sâu, bạn sẽ không được nhân bản các nút sâu hơn, nếu chúng là các tham chiếu. Có thể không phải là một vấn đề trong trường hợp của bạn, nhưng chỉ cần ghi nhớ nó.
troelskn

4
@troelskn Tôi đã sửa nó bằng cách thêm một số đệ quy. Hàm này bây giờ sẽ hoạt động trên bất kỳ loại mảng nào, kể cả các loại hỗn hợp. Nó cũng hoạt động tốt cho các mảng đơn giản, vì vậy nó không còn được bản địa hóa nữa. Về cơ bản, nó là một máy nhân bản mảng phổ quát. Bạn vẫn cần xác định hàm __clone () trong các đối tượng của mình nếu chúng sâu, nhưng vượt quá "phạm vi" của hàm này (xin lỗi vì chơi chữ xấu).
Andrew Larsson

2
Tôi tin tưởng mạnh mẽ rằng đây là câu trả lời thực sự cho câu hỏi này, Cách duy nhất tôi thấy để thực sự sao chép sâu một mảng có chứa các đối tượng.
Patrick

Nó không lặp lại các thuộc tính đối tượng có thể có các mảng và các đối tượng được tham chiếu khác.
ya.teck

6
Việc sử dụng __FUNCTION__này là tuyệt vời.
zessx

29

Khi bạn làm

$array_x = $array_y;

PHP sao chép mảng, vì vậy tôi không chắc bạn sẽ bị đốt cháy như thế nào. Đối với trường hợp của bạn,

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

nên làm việc tốt

Để bị đốt cháy, tôi nghĩ bạn sẽ phải sử dụng các tài liệu tham khảo hoặc mong muốn các đối tượng bên trong các mảng được nhân bản.


12
+1 cho điều này: "hoặc mong muốn các đối tượng bên trong mảng được nhân bản"
Melsi


18

đơn giản và làm cho bản sao sâu phá vỡ tất cả các liên kết

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

4
Nói chung, nó hoạt động tốt, tuy nhiên trong một số trường hợp, nó có thể đưa ra một ngoại lệ vì không phải tất cả các biến đều được tuần tự hóa (ví dụ như đóng cửa và kết nối cơ sở dữ liệu).
ya.teck

Một điều cần lưu ý là các tham chiếu đối tượng có thể được khôi phục nếu một lớp thực hiện phương thức ma thuật __wakeup.
ya.teck

Cảm ơn, cuối cùng, một cái gì đó thực sự hoạt động, không phải là các câu trả lời bollock khác có rất nhiều sự ủng hộ, họ chắc chắn đã không xử lý mảng các đối tượng như được chỉ định trong câu hỏi số lượng phần tử trong mảng có thể thay đổi, nhưng chắc chắn không phải là tham chiếu đến các đối tượng bên trong chúng
FentomX1

12

Tôi thích array_replace(hoặc array_replace_recursive).

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

Nó hoạt động như Object.assigntừ 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';

sẽ cho kết quả

// 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
Điều gì về array_slice($arr, 0)hoặc khi bạn không quan tâm đến chìa khóa , array_values($arr)? Tôi nghĩ rằng họ có thể nhanh hơn tìm kiếm trong một mảng. Ngoài ra, trong javascript, nó khá phổ biến để sử dụng Array.slice()để sao chép mảng.
Christian

Trong JS, chúng ta có Object cho cặp khóa-giá trị và Mảng . PHP không tạo ra sự khác biệt này. Đối với các mảng PHP có các chỉ mục được đánh số array_slicevà tất cả các phương thức khác được đề cập ở đây hoạt động rất tốt. Nhưng nếu bạn muốn hợp nhất một số cặp khóa-giá trị (vì nó cũng có thể với các Đối tượng JS thông qua Object.assignhoặc cú pháp trải rộng ), array_replacecó thể hữu ích hơn.
Putzi San

@Christian cảm ơn bạn đã gợi ý trong array_values()đó hoạt động hoàn hảo cho trường hợp sử dụng của tôi.
bigsee

11

Nếu bạn chỉ có các loại cơ bản trong mảng của mình, bạn có thể làm điều này:

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

Bạn sẽ không cần cập nhật các tài liệu tham khảo theo cách thủ công
Tôi biết nó sẽ không hiệu quả với tất cả mọi người, nhưng nó hiệu quả với tôi


4
+1 đây là một điều thực sự tồi tệ để làm, nhưng về mặt kỹ thuật là chính xác và thông minh. Nếu tôi thấy mã này, tôi sẽ đối mặt với lòng bàn tay nhưng tôi không thể giúp được.
Phản ứng

4

Vì điều này không được đề cập trong bất kỳ câu trả lời nào và hiện có sẵn trong PHP 5.3 (giả sử Bài gốc được sử dụng 5.2).

Để duy trì cấu trúc mảng và thay đổi giá trị của nó, tôi thích sử dụng array_replacehoặc array_replace_recursivetùy thuộc vào trường hợp sử dụng của tôi.

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

Dưới đây là một ví dụ sử dụng array_replacearray_replace_recursivechứng minh rằng nó có thể duy trì thứ tự được lập chỉ mục và có khả năng xóa tham chiếu.

http://ideone.com/SzlBUZ

Mã dưới đây được viết bằng cú pháp mảng ngắn có sẵn kể từ PHP 5.4 thay thế array()bằng []. http://php.net/manual/en/lingu.types.array.php

Hoạt động trên các mảng được lập chỉ mục và tên được bù

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

Đầu ra:

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

Đây là cách tôi sao chép mảng của mình trong Php:

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

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

Kết quả này:

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

2
Tại sao không chỉ nói $test2 = $test;? Vấn đề gì đang ArrayObjectgiải quyết ở đây?
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

Cách an toàn và rẻ nhất tôi tìm thấy là:

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

Điều này cũng có lợi ích để reindex mảng.

Điều này sẽ không hoạt động như mong đợi trên mảng kết hợp (băm), nhưng hầu hết các câu trả lời trước đó.


1

Tạo một bản sao của 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);

?>

từ https://www.php.net/manual/en/arrayobject.getarraycopy.php


0

Xác định điều này:

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

Sao chép $ _ARRAY sang $ _ARRAY2:

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

0

Trong mảng php, bạn chỉ cần gán chúng cho biến khác để lấy bản sao của mảng đó. Nhưng trước tiên, bạn cần chắc chắn về kiểu của nó, cho dù đó là mảng hay mảngObject hay stdObject.

Đối với mảng php đơn giản:

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

Chỉ cần đăng thêm một giải pháp;)


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

Bảo tồn cả chìa khóa và giá trị. Mảng 'a' là bản sao chính xác của mảng 'b'

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.