如何在保持顺序的情况下更改数组中的键?

20

我该如何做到这一点:

$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!

我没有看到有一个能做到这个的函数。 有一种方法可以做到吗?

5个回答

29

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;
}

3
@Nazariy说:在PHP中,如果原始参数未被修改,则不会复制它(这称为写时复制,COW)。从这个角度来看,在这种情况下使用引用我并没有看到任何优势。 - zerkms
@zerkms,我在想,如果我的数组的前两个索引是字符串,第三个是整数,其余的都是字符串,你的函数还能用吗? - Memor-X
3
@Memor-X:所以你不是在尝试,而是假设我会替你完成这件事,而你则一边观看有小猫咪的YouTube视频? - zerkms
我也遇到了索引与命名键混合的问题。我发布了一个回答来解决这个问题。 - Dieter Gribnitz
@DieterGribnitz 你只需要将 true 作为 array_search() 的第三个参数传递进去即可;完成了 :) - Ja͢ck
显示剩余5条评论

4

被接受的答案的逻辑存在缺陷。

如果你有这样一个数组:

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

如果你将'k4'替换为'something',你将得到以下输出:
[
    '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;
}

编辑:2014/12/03 如果将array_search函数的第三个参数(strict)设置为true,那么接受的答案是有效的。


1
正确,但是在array_keys中简单地将“strict”标志设置为true就可以获得所需的输出并节省8行代码。 将$index = array_search($key1, $keys);更改为$index = array_search($key1, $keys, true);会生成正确的结果: [ 'k1'=>'k1', 'k2'=>'k2', 0 =>'k3', 'something'=>'k4' ] - CragMonkey
2
谢谢,你说得对。我最终也想到了这一点,但忘记了这篇文章。现在会更新它。 - Dieter Gribnitz
只是想感谢您为此所做的努力...非常感激! - salih0vicX

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

2

使用 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;
}

1

已经有了一个不错的答案,但我想再加入我的观点:

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

我一直在努力提高自己 :)


我认为不应该硬编码数值。 - Mike Purcell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接