มีฟังก์ชั่นในการทำสำเนาอาร์เรย์ PHP ไปยังอีกชุดหรือไม่?


529

มีฟังก์ชั่นในการทำสำเนาอาร์เรย์ PHP ไปยังอีกชุดหรือไม่?

ฉันถูกเขียนไปหลายครั้งพยายามคัดลอกอาร์เรย์ PHP ฉันต้องการคัดลอกอาร์เรย์ที่กำหนดไว้ภายในวัตถุไปยังโกลบอลด้านนอก


ช้ามาก ๆ แต่ในสภาพแวดล้อมของฉันฉันทดสอบ (และใช้งานได้): function arrayCopy (array $ a) {return $ a; } $ a1 = array (); สำหรับ ($ i = 0; $ i <3; $ i ++) {$ a1 ["key- $ i"] = "ค่า # $ i"; } $ a1 ["key-sub-array"] = array (1, 2, 3, 4); $ a2 = $ a1; $ a3 = arrayCopy ($ a1); สำหรับ ($ i = 0; $ i <3; $ i ++) {ถ้า (! is_array ($ a2 ["key- $ i"])) {$ a2 ["key- $ i"] = "เปลี่ยนค่า # $ ผม"; }} $ a2 ["key-sub-array"] = array ("เปลี่ยนแปลง sub-array 1", "เปลี่ยนแปลง sub-array 2"); var_dump ($ A1); var_dump ($ a2); var_dump ($ a3); เคล็ดลับคือการไม่ส่งผ่านอาร์เรย์เพื่อใช้อ้างอิงในฟังก์ชั่น ;-)
Sven

คำตอบ:


926

ในอาร์เรย์ PHP ได้รับมอบหมายโดยการคัดลอกในขณะที่วัตถุได้รับมอบหมายโดยการอ้างอิง ซึ่งหมายความว่า:

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

จะให้ผล:

array(0) {
}

โดย:

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

อัตราผลตอบแทน:

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

คุณอาจสับสนกับความสลับซับซ้อนเช่นArrayObjectซึ่งเป็นวัตถุที่ทำหน้าที่เหมือนกับอาเรย์ อย่างไรก็ตามในการเป็นวัตถุมันมีความหมายอ้างอิง

แก้ไข: @AndrewLarsson ยกประเด็นในความคิดเห็นด้านล่าง PHP มีคุณสมบัติพิเศษที่เรียกว่า "การอ้างอิง" พวกมันค่อนข้างคล้ายกับพอยน์เตอร์ในภาษาเช่น C / C ++ แต่ไม่เหมือนกัน หากอาเรย์ของคุณมีการอ้างอิงในขณะที่อาเรย์ของตัวเองถูกส่งผ่านโดยการคัดลอกการอ้างอิงจะยังคงแก้ไขไปยังเป้าหมายเดิม แน่นอนว่าเป็นพฤติกรรมที่ต้องการ แต่ฉันคิดว่ามันคุ้มค่าที่จะกล่าวถึง


104
คุณไม่ได้ตอบคำถาม คุณอธิบายปัญหาเท่านั้น ซึ่งสำหรับ OP นั้นน่าจะเป็นสิ่งที่เขาต้องการมากที่สุด อย่างไรก็ตามสำหรับฉัน (และคนอื่น ๆ ด้วย) มาที่นี่เกือบสี่ปีต่อมาด้วยปัญหาที่คล้ายกันฉันยังไม่มีวิธีที่ดีในการโคลนอาร์เรย์โดยไม่ต้องแก้ไขอาร์เรย์เดิม (ซึ่งรวมถึงพอยน์เตอร์ภายในด้วย) ฉันคิดว่ามันเป็นเวลาที่ฉันจะถามคำถามของตัวเอง
Andrew Larsson

28
@AndrewLarsson แต่ PHP ทำตามค่าเริ่มต้น - นั่นคือส่วนสำคัญของมัน การอ้างอิงยังไม่ได้รับการแก้ไขดังนั้นหากคุณต้องการคุณจะต้องสำรวจอาร์เรย์ซ้ำและสร้างอาร์เรย์ขึ้นใหม่ - เช่นเดียวกันหากอาร์เรย์ต้นทางมีวัตถุและคุณต้องการโคลนเหล่านั้นคุณต้องทำด้วยตนเอง โปรดจำไว้ว่าการอ้างอิงใน PHP นั้นไม่เหมือนกับพอยน์เตอร์ใน C โดยไม่ทราบอะไรเกี่ยวกับกรณีของคุณฉันขอแนะนำว่ามันแปลกไหมที่มีอาร์เรย์ของการอ้างอิงในกรณีแรกโดยเฉพาะอย่างยิ่งถ้าคุณไม่ต้องการรักษา พวกเขาเป็นข้อมูลอ้างอิง? กรณีการใช้งานคืออะไร?
troelskn

1
@troelskn ฉันได้เพิ่มคำตอบสำหรับคำถามนี้ด้วยวิธีแก้ไขปัญหาของฉัน: stackoverflow.com/a/17729234/1134804
Andrew Larsson

3
แต่ถ้าเป็นพฤติกรรมที่ไม่ต้องการ คำถามถามถึงวิธีการทำสำเนาลึก เห็นได้ชัดว่าไม่ต้องการ คำตอบของคุณไม่ดีไปกว่า: $copy = $original;. ซึ่งไม่ทำงานหากองค์ประกอบอาเรย์อ้างอิง
doug65536

8
และเช่นเคยphpนำเสนอเรามีผลที่คาดหวังน้อยเพราะการแก้ปัญหานี้ไม่ได้เสมอไป $a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];พิมพ์array0ขณะพิมพ์$a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"]; array1เห็นได้ชัดว่าบางอาร์เรย์จะถูกคัดลอกโดยอ้างอิง
Tino

186

PHP จะคัดลอกอาร์เรย์โดยค่าเริ่มต้น การอ้างอิงใน PHP ต้องมีความชัดเจน

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

หากต้องการใช้การอ้างอิงอาจมีความสำคัญหากอาร์เรย์มีขนาดใหญ่ ฉันไม่แน่ใจ แต่ฉันคิดว่ามันควรจะนำไปสู่การใช้หน่วยความจำน้อยลงและประสิทธิภาพที่ดีขึ้น (ไม่จำเป็นต้องคัดลอกทั้งอาร์เรย์ในหน่วยความจำ)
robsch

11
@robsch - ที่ระดับตรรกะของโปรแกรมอาร์เรย์จะถูกคัดลอก แต่ในความทรงจำมันจะไม่ถูกคัดลอกจนกว่ามันจะถูกแก้ไขเพราะ PHP ใช้ซีแมนทิกส์ copy-on-write สำหรับทุกประเภท stackoverflow.com/questions/11074970/…
Jessica Knight

@CoreyKnight รู้ดี ขอบคุณสำหรับสิ่งนี้.
robsch

4
โปรดทราบว่านี่ไม่เป็นความจริงสำหรับอาร์เรย์ที่ซ้อนกันมันเป็นข้อมูลอ้างอิงดังนั้นคุณจึงจบลงด้วยความยุ่งเหยิง
MightyPork

45

หากคุณมีอาร์เรย์ที่มีวัตถุคุณต้องทำสำเนาของอาร์เรย์นั้นโดยไม่ต้องสัมผัสตัวชี้ภายในและคุณต้องการให้วัตถุทั้งหมดถูกโคลน (เพื่อที่ว่าคุณจะไม่แก้ไขต้นฉบับเมื่อคุณทำการเปลี่ยนแปลงคัดลอก อาร์เรย์) ใช้สิ่งนี้

เคล็ดลับที่จะไม่สัมผัสตัวชี้ภายในของอาเรย์คือการตรวจสอบให้แน่ใจว่าคุณกำลังทำงานกับสำเนาของอาเรย์และไม่ใช่อาเรย์ดั้งเดิม (หรือการอ้างอิงถึงอาเรย์) ดังนั้นการใช้พารามิเตอร์ฟังก์ชั่นจะทำให้งานเสร็จ นี่คือฟังก์ชั่นที่ใช้ในอาเรย์)

โปรดทราบว่าคุณยังต้องใช้__clone ()บนวัตถุของคุณหากคุณต้องการให้คุณสมบัติของพวกมันถูกโคลน

ฟังก์ชั่นนี้ใช้ได้กับอาเรย์ทุกประเภท (รวมถึงแบบผสม)

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

1
โปรดทราบว่านี่เป็นกรณีพิเศษเล็กน้อย นอกจากนี้โปรดทราบว่านี่จะโคลนการอ้างอิงระดับแรกเท่านั้น หากคุณมีอาร์เรย์ที่ลึกคุณจะไม่ได้รับโหนดที่ลึกกว่าหากพวกมันเป็นข้อมูลอ้างอิง อาจไม่เป็นปัญหาในกรณีของคุณ แต่ให้จำไว้
troelskn

4
@troelskn ฉันแก้ไขโดยเพิ่มการสอบถามซ้ำ ตอนนี้ฟังก์ชั่นนี้จะทำงานกับอาเรย์ประเภทใดก็ได้รวมถึงประเภทผสม นอกจากนี้ยังใช้งานได้ดีสำหรับอาร์เรย์ที่เรียบง่ายดังนั้นจึงไม่แปลเป็นภาษาท้องถิ่นอีกต่อไป มันเป็นเครื่องโคลนนิ่งอาร์เรย์ทั่วไป คุณยังคงต้องกำหนดฟังก์ชัน __clone () ในวัตถุของคุณหากอยู่ลึก แต่นั่นอยู่เหนือ "ขอบเขต" ของฟังก์ชันนี้ (ขออภัยสำหรับปุนที่ไม่ดี)
Andrew Larsson

2
ฉันเชื่ออย่างยิ่งว่านี่คือคำตอบที่แท้จริงสำหรับคำถามนี้วิธีเดียวที่ฉันเห็นเพื่อคัดลอกอาร์เรย์ที่มีวัตถุจริงๆ
แพทริค

มันไม่ได้ทำซ้ำคุณสมบัติของวัตถุซึ่งอาจมีอาร์เรย์อื่น ๆ และวัตถุที่อ้างอิง
ya.teck

6
การใช้งาน__FUNCTION__นี้ยอดเยี่ยม
zessx

29

เมื่อคุณทำ

$array_x = $array_y;

PHP คัดลอกอาเรย์ดังนั้นฉันไม่แน่ใจว่าคุณจะเผาไหม้อย่างไร สำหรับกรณีของคุณ

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

ควรทำงานได้ดี

เพื่อที่จะได้เผาฉันจะคิดว่าคุณอาจต้องใช้การอ้างอิงหรือคาดหวังว่าวัตถุภายในอาร์เรย์จะถูกโคลน


12
+1 สำหรับสิ่งนี้: "หรือคาดหวังว่าวัตถุภายในอาร์เรย์จะถูกโคลน"
Melsi

20

array_merge() เป็นฟังก์ชันที่คุณสามารถคัดลอกอาร์เรย์หนึ่งไปยังอีกใน PHP


4
ใช่ แต่คีย์จะถูกแก้ไข quote: ค่าในอาร์เรย์อินพุตที่มีคีย์ตัวเลขจะถูกกำหนดหมายเลขใหม่ด้วยคีย์ที่เพิ่มขึ้นโดยเริ่มจากศูนย์ในอาร์เรย์ผลลัพธ์
zamnuts

@zamnuts $a_c = array_combine(array_keys($a), array_values($a))สำหรับการรักษากุญแจ:
CPHPython

18

ง่ายและทำให้การคัดลอกลึกทำลายลิงค์ทั้งหมด

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

4
โดยทั่วไปแล้วจะใช้งานได้ดี แต่ในบางกรณีอาจมีข้อยกเว้นเนื่องจากตัวแปรบางตัวนั้นไม่สามารถทำอนุกรมได้ (เช่นการปิดและการเชื่อมต่อฐานข้อมูล)
ya.teck

สิ่งที่ควรทราบอีกประการหนึ่งคือการอ้างอิงวัตถุสามารถกู้คืนได้หากคลาสดำเนินการ __wakeup magic
ya.teck

ขอบคุณในที่สุดบางสิ่งที่ใช้งานได้จริงไม่ใช่คำตอบของ bollock อื่น ๆ ที่มี upvotes จำนวนมากพวกเขาไม่ได้จัดการกับอาเรย์ของวัตถุตามที่ระบุไว้ในคำถามที่จำนวนขององค์ประกอบในอาเรย์อาจเปลี่ยนแปลง แต่แน่นอนไม่อ้างอิงถึง วัตถุที่อยู่ภายในพวกเขา
FentomX1

12

ฉันชอบarray_replace(หรือarray_replace_recursive)

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

มันทำงานได้เหมือนObject.assignจาก 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';

จะส่งผลให้

// 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
แล้วarray_slice($arr, 0)เมื่อไหร่ที่คุณไม่ใส่ใจเรื่องกุญแจarray_values($arr)? ฉันคิดว่าพวกเขาอาจเร็วกว่าการค้นหาในอาเรย์ นอกจากนี้ในจาวาสคริปต์ก็ค่อนข้างนิยมใช้Array.slice()โคลนอาร์เรย์
Christian

ใน JS เรามีวัตถุสำหรับคีย์มูลค่าคู่และอาร์เรย์ PHP ไม่ได้สร้างความแตกต่างนี้ สำหรับอาร์เรย์ PHP ที่มีดัชนีตัวเลขarray_sliceและวิธีการอื่น ๆ ที่กล่าวถึงในที่นี้ทำงานได้ดีมาก แต่ถ้าคุณต้องการผสานคู่ของคีย์ - ค่า - คู่ (เพราะมันเป็นไปได้กับ JS-Objects ผ่านObject.assignหรือไวยากรณ์สเปรด ) array_replaceอาจมีประโยชน์มากกว่า
Putzi San

@ คริสเตียนขอบคุณสำหรับข้อเสนอแนะarray_values()ที่ทำงานได้อย่างสมบูรณ์แบบสำหรับกรณีการใช้งานของฉัน
bigsee

11

หากคุณมีประเภทพื้นฐานเท่านั้นในอาเรย์ของคุณคุณสามารถทำได้:

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

คุณไม่จำเป็นต้องอัปเดตข้อมูลอ้างอิงด้วยตนเอง
ฉันรู้ว่ามันจะไม่ทำงานสำหรับทุกคน แต่มันใช้งานได้สำหรับฉัน


4
+1 นี่เป็นสิ่งที่เลวร้ายจริงๆที่ต้องทำ แต่ถูกต้องและฉลาดในทางเทคนิค ถ้าฉันเห็นสิ่งนี้ในรหัสฉันจะเผชิญฝ่ามือ แต่ฉันไม่สามารถช่วยได้ แต่ชอบ
Reactgular

4

เนื่องจากสิ่งนี้ไม่ได้ครอบคลุมในคำตอบใด ๆ และขณะนี้มีให้ใน PHP 5.3 (สันนิษฐานว่าโพสต์ต้นฉบับใช้ 5.2)

เพื่อรักษาโครงสร้างอาเรย์และเปลี่ยนค่าของมันฉันชอบที่จะใช้array_replaceหรือarray_replace_recursiveขึ้นอยู่กับกรณีการใช้ของฉัน

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

นี่คือตัวอย่างการใช้array_replaceและการarray_replace_recursiveแสดงให้เห็นถึงความสามารถในการรักษาลำดับดัชนีและความสามารถในการลบการอ้างอิง

http://ideone.com/SzlBUZ

โค้ดข้างล่างนี้เขียนโดยใช้ไวยากรณ์อาร์เรย์สั้นมีตั้งแต่ PHP 5.4 ซึ่งแทนที่ด้วย array() http://php.net/manual/en/language.types.array.php[]

ใช้งานได้ทั้งอ็อฟเซ็ตดัชนีและออฟเซ็ตที่จัดทำดัชนีชื่อ

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

เอาท์พุท:

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

3

ฉันรู้เรื่องนี้เมื่อนานมาแล้ว แต่สิ่งนี้ได้ผลสำหรับฉัน ..

$copied_array = array_slice($original_array,0,count($original_array));

2

นี่คือวิธีที่ฉันกำลังคัดลอกอาร์เรย์ของฉันใน Php:

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

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

ผลลัพธ์นี้:

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

2
ทำไมไม่เพียงแค่พูดว่า$test2 = $test;? มีปัญหาอะไรArrayObjectแก้ที่นี่?
เนท

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

วิธีที่ปลอดภัยที่สุดและถูกที่สุดที่ฉันพบคือ:

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

สิ่งนี้มีประโยชน์ในการทำดัชนีอาร์เรย์อีกครั้ง

สิ่งนี้จะไม่ทำงานตามที่คาดไว้ในอาเรย์แบบเชื่อมโยง (แฮช) แต่ไม่ใช่คำตอบก่อนหน้านี้ส่วนใหญ่




0

ในอาเรย์ของ php คุณต้องกำหนดให้ตัวแปรอื่น ๆ เพื่อรับสำเนาของอาเรย์นั้น แต่ก่อนอื่นคุณต้องตรวจสอบให้แน่ใจเกี่ยวกับประเภทของมันไม่ว่าจะเป็นอาร์เรย์หรือ arrayObject หรือ stdObject

สำหรับอาร์เรย์ php อย่างง่าย:

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

$b = $a;

var_dump($b);

output:

array:1 [
  "data" => 10
]



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

รักษาทั้งคีย์และค่า Array 'a' เป็นสำเนาที่แน่นอนของอาร์เรย์ 'b'

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.