Есть ли функция, чтобы сделать копию массива PHP в другой?

460

Есть ли функция, чтобы сделать копию массива PHP другой?

Я несколько раз сжег, пытаясь скопировать массивы PHP. Я хочу скопировать массив, определенный внутри объекта, глобально за его пределами.

  • 0
    очень поздно, но в моей среде я протестировал это (и это сработало): function arrayCopy (array $ a) {return $ a; } $ a1 = array (); for ($ i = 0; $ i <3; $ i ++) {$ a1 ["key- $ i"] = "value # $ 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"] = "измененное значение # $ я"; }} $ a2 ["key-sub-array"] = array ("измененный подмассив 1", "измененный подмассив 2"); var_dump ($ a1); var_dump ($ a2); var_dump ($ a3); Хитрость в том, чтобы не передавать массив в качестве ссылки в функцию ;-)
Теги:
arrays
copy

17 ответов

792
Лучший ответ

В 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/С++, но не совсем одинаковы. Если ваш массив содержит ссылки, тогда, когда сам массив передается копией, ссылки будут по-прежнему разрешаться исходной цели. Это, конечно, обычно желаемое поведение, но я думал, что стоит упомянуть.

  • 78
    Вы не ответили на вопрос. Вы только объяснили проблему. Что для ОП, скорее всего, то, что он искал. Однако для меня (и других тоже), приехав сюда почти четыре года спустя с аналогичной проблемой, у меня все еще нет хорошего способа клонировать массив без изменения исходного массива (который также включает в себя внутренние указатели). Я полагаю, пришло время задать свой вопрос.
  • 23
    @AndrewLarsson Но PHP делает это по умолчанию - в этом суть. Однако ссылки не разрешаются, поэтому, если вам это нужно, вам придется рекурсивно проходить по массиву и создавать новый - аналогично, если исходный массив содержит объекты и вы хотите, чтобы они были клонированы, вам придется делать это вручную. Помните также, что ссылки в PHP не совпадают с указателями в C. Не зная ничего о вашем случае, могу ли я предположить, что странно иметь массив ссылок в первом случае, особенно если вы не собираетесь рассматривать их как ссылки? Какой вариант использования?
Показать ещё 10 комментариев
163

PHP будет копировать массив по умолчанию. Ссылки на PHP должны быть явными.

$a = array(1,2);
$b = $a; // $b will be a different array
$c = &$a; // $c will be a reference to $a
  • 0
    Использование ссылки может быть важно, если массив огромен. Я не уверен, но я предполагаю, что это должно привести к меньшему потреблению памяти и лучшей производительности (не нужно копировать весь массив в памяти).
  • 7
    @robsch - на уровне логики программы массив копируется. Но в памяти он фактически не будет скопирован, пока не будет изменен - потому что PHP использует семантику копирования при записи для всех типов. stackoverflow.com/questions/11074970/...
Показать ещё 2 комментария
39

Если у вас есть массив, содержащий объекты, вам нужно сделать копию этого массива, не касаясь его внутреннего указателя, и вам необходимо клонировать все объекты (чтобы не изменять оригиналы при внесении изменений в скопированный объект). массив), используйте это.

Хитрость в том, чтобы не касаться внутреннего указателя массива, заключается в том, чтобы убедиться, что вы работаете с копией массива, а не с исходным массивом (или ссылкой на него), поэтому использование параметра функции позволит выполнить работу (таким образом, это функция, которая принимает массив).

Обратите внимание, что вам все равно нужно будет реализовать __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
    Имейте в виду, что это немного особый случай. Также обратите внимание, что это будет клонировать только ссылки первого уровня. Если у вас есть глубокий массив, вы не сможете клонировать более глубокие узлы, если они являются ссылками. Может не быть проблемой в вашем случае, но просто имейте это в виду.
  • 4
    @troelskn Я исправил это, добавив некоторую рекурсию. Эта функция теперь будет работать с любым типом массива, включая смешанные типы. Это также работает для простых массивов, так что больше не локализуется. Это в основном универсальная машина для клонирования массивов. Вам все еще нужно определить функцию __clone () в ваших объектах, если они глубоки, но это выходит за рамки этой функции (извините за плохой каламбур).
Показать ещё 13 комментариев
27

Когда вы делаете

$array_x = $array_y;

PHP копирует массив, поэтому я не уверен, как вы сожгли бы. Для вашего случая,

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

должен работать нормально.

Чтобы сгореть, я думаю, вам придется либо использовать ссылки, либо ожидать, что объекты внутри массивов будут клонированы.

  • 11
    +1 за это: «или ожидая, что объекты внутри массивов будут клонированы»
17

array_merge() - это функция, в которой вы можете скопировать один массив в другой в PHP.

  • 4
    да, но ключи будут изменены, цитата: Значения во входном массиве с цифровыми ключами будут перенумерованы с увеличивающимися ключами, начинающимися с нуля в результирующем массиве.
  • 0
    @zamnuts для поддержки ключей: $a_c = array_combine(array_keys($a), array_values($a)) .
12

простой и делает глубокую копию всех ссылок

$new=unserialize(serialize($old));
  • 3
    Обычно он работает нормально, однако в некоторых случаях он может генерировать исключение, потому что не все переменные являются сериализуемыми (например, замыкания и соединения с базой данных).
  • 0
    Следует также отметить, что ссылки на объекты могут быть восстановлены, если класс реализует магический метод __wakeup.
Показать ещё 1 комментарий
8

просто и аккуратно для PHP> = 5.3

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

array_replace (или array_replace_recursive). Для меня самый чистый путь и просто как 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_values($arr) ? Я думаю, что они могут быть быстрее, чем поиск в массиве. Кроме того, в javascript довольно популярно использование Array.slice() для клонирования массивов.
  • 0
    В JS у нас есть Object для пар ключ-значение и Array . PHP не делает эту разницу. Для массивов PHP с пронумерованными индексами array_slice и все другие методы, упомянутые здесь, работают очень хорошо. Но если вы хотите объединить несколько пар ключ-значение (как это также возможно с JS-Objects через Object.assign или синтаксис распространения ), array_replace может быть более полезным.
Показать ещё 1 комментарий
8

Если у вас есть только базовые типы в вашем массиве, вы можете сделать это:

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

Вам не нужно вручную обновлять ссылки
Я знаю, что это не сработает для всех, но это сработало для меня.

  • 3
    +1 это действительно плохая вещь, но технически правильная и умная. Если бы я видел это в коде, я столкнулся бы с пальмой, но я не могу не любить это.
4

Так как это не было затронуто ни в одном из ответов и теперь доступно в PHP 5.3 (предполагается, что Original Post использовала 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 
$b = array_values($a);

Это также дает возможность переиндексировать массив.

Это не будет работать так, как ожидалось, в ассоциативном массиве (хэш), но ни один из предыдущих ответов.

1

Определите это:

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

Скопируйте $_ARRAY в $_ARRAY2:

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

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

Просто чтобы опубликовать еще одно решение;)

0

В php-массиве вам нужно просто назначить их другой переменной, чтобы получить копию этого массива. Но сначала вам нужно убедиться в этом, будь то массив или arrayObject или stdObject.

Для простого массива php:

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

$b = $a;

var_dump($b);

output:

array:1 [
  "data" => 10
]
0
<?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;
}
?>
-2
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;
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню