Как изменить ключ в массиве при сохранении порядка?

Как я могу это сделать:

$array = array('a' => 1, 'd' => 2, 'c' => 3); //associative array

// rename $array['d'] as $array['b']
$array = replace_key_function($array, 'd', 'b');

var_export($array); // array('a' => 1, 'b' => 2, 'c' => 3); same order!

Я не видел функцию, которая делает это. Есть ли способ сделать это?

Ответ 1

http://ideone.com/nCZnY

$array = array('a' => 1, 'd' => 2, 'c' => 3); //associative array

// rename $array['d'] as $array['b']
$array = replace_key_function($array, 'd', 'b');

var_export($array); // array('a' => 1, 'b' => 2, 'c' => 3); same order!

function replace_key_function($array, $key1, $key2)
{
    $keys = array_keys($array);
    $index = array_search($key1, $keys);

    if ($index !== false) {
        $keys[$index] = $key2;
        $array = array_combine($keys, $array);
    }

    return $array;
}

Ответ 2

В логике принятого ответа есть недостаток.

Если у вас есть такой массив:

[
    'k1'=>'k1',
    'k2'=>'k2',
    'k3',
    'k4'=>'k4'
]

и замените "k4" на "что-то", вы получите такой вывод:

[
    'k1'=>'k1',
    'k2'=>'k2',
    'something' => 'k3',
    'k4'=>'k4'
]

Вот быстрое решение, которое решает проблему:

function replace_key_function($array, $key1, $key2)
{
    $keys = array_keys($array);
    //$index = array_search($key1, $keys);        
    $index = false;
    $i = 0;
    foreach($array as $k => $v){
        if($key1 === $k){
            $index = $i;
            break;
        }
        $i++;
    }

    if ($index !== false) {
        $keys[$index] = $key2;
        $array = array_combine($keys, $array);
    }

    return $array;
}

EDIT: 2014/12/03 Принятый ответ работает, если вы установите третий параметр (строгий) array_search в true.

Ответ 3

Вместо использования циклов вы всегда можете сгладить строку с помощью json_encode(), выполнить замену строки, затем json_decode() вернуться к массиву:

function replaceKey($array, $old, $new)
{  
    //flatten the array into a JSON string
    $str = json_encode($array);

    // do a simple string replace.
    // variables are wrapped in quotes to ensure only exact match replacements
    // colon after the closing quote will ensure only keys are targeted 
    $str = str_replace('"'.$old.'":','"'.$new.'":',$str);

    // restore JSON string to array
    return json_decode($str, TRUE);       
}

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

$arr = array(
    array(
         'name'     => 'Steve'
        ,'city'     => 'Los Angeles'
        ,'state'    => 'CA'
        ,'country'  => 'USA'
        ,'mother'   => array(
             'name'     => 'Jessica'
            ,'city'     => 'San Diego'
            ,'state'    => 'CA'
            ,'country'  => 'USA'
        )
    )
    ,array(
         'name'     => 'Sara'
        ,'city'     => 'Seattle'
        ,'state'    => 'WA'
        ,'country'  => 'USA'
        ,'father'   =>  array(
             'name'     => 'Eric'
            ,'city'     => 'Atlanta'
            ,'state'    => 'GA'
            ,'country'  => 'USA'
            ,'mother'   => array(
                 'name'     => 'Sharon'
                ,'city'     => 'Portland'
                ,'state'    => 'OR'
                ,'country'  => 'USA'
            )
        )
    )
);
$replaced = replaceKey($arr,'city','town');
print_r($replaced);

выходы

Array
(
    [0] => Array
        (
            [name] => Steve
            [town] => Los Angeles
            [state] => CA
            [country] => USA
            [mother] => Array
                (
                    [name] => Jessica
                    [town] => San Diego
                    [state] => CA
                    [country] => USA
                )
        )

    [1] => Array
        (
            [name] => Sara
            [town] => Seattle
            [state] => WA
            [country] => USA
            [father] => Array
                (
                    [name] => Eric
                    [town] => Atlanta
                    [state] => GA
                    [country] => USA
                    [mother] => Array
                        (
                            [name] => Sharon
                            [town] => Portland
                            [state] => OR
                            [country] => USA
                        )
                )
        )
)

Ответ 4

Общее и простое решение с PHP 5.3+ с помощью array_walk:

$array = array('a' => 1, 'd' => 2, 'c' => 3); //associative array

$array = replace_keys($array, array('d' => 'b'));
var_export($array); // array('a' => 1, 'b' => 2, 'c' => 3); same order!

function replace_keys(array $source, array $keyMapping) {
    $target = array();
    array_walk($source,
               function ($v, $k, $keyMapping) use (&$target) {
                    $mappedKey = isset($keyMapping[$k]) ? $keyMapping[$k] : $k;
                    $target[$mappedKey] = $v;
               },
               $keyMapping);
    return $target;
}

Ответ 5

был опубликован хороший ответ, но здесь мои два пенса:

$array = array('a'=>1, 'd'=>2, 'c'=>3);
// rename 'd' to 'b'
foreach($array as $k=>$v){
    if($k == 'd') { $k='b'; }
        $newarray[$k] = $v;
}
$array = $newarray;

в ответ на mike-purcell это было бы более приемлемым подходом к моему примеру выше?

changeKey($array, 'd', 'b');

function changeKey($array, $oldKey, $newKey)
{
    foreach($array as $k=>$v){
        if($k == $oldKey) { $k = $newKey; }
        $returnArray[$k] = $v;
    }
    return $returnArray;
}

Я всегда стараюсь улучшить:)