25

You can "change" the key of an array element simply by setting the new key and removing the old:

$array[$newKey] = $array[$oldKey];
unset($array[$oldKey]);

But this will move the key to the end of the array.

Is there some elegant way to change the key without changing the order?

(PS: This question is just out of conceptual interest, not because I need it anywhere.)

Community
  • 1
  • 1
NikiC
  • 100,734
  • 37
  • 191
  • 225
  • I'd imagine some ugly construct with array_splice() and array_slice() would do the trick – Marc B Jan 16 '12 at 17:49
  • @MarcB That wouldn't work with string keys though. – NikiC Jan 16 '12 at 17:51
  • I'm not a PHP programmer, but what in the world are the semantics of `$arr[$oldKey]` if this works as an argument to a function which removes `$oldKey` from `$arr`? I think PHP might be more interesting than I previously thought, will have to look into this … – Felix Dombek Jan 16 '12 at 19:53
  • 1
    @FelixDombek I'm not sure I get you. `$array[$oldKey]` will just return the value with the key `$oldKey`. – NikiC Jan 16 '12 at 20:00
  • 1
    @NikiC That's what I also thought, but if it evaluates to, say, 5, then how does `unset()` delete the element from the array? How does PHP even know in *which* array it should delete the value 5? – Felix Dombek Jan 16 '12 at 20:04
  • Ah, that's what you mean. `unset` is not a normal function, it's a language construct. So it's not like `$array[$oldKey]` is evaluated and passed to `unset()`. – NikiC Jan 16 '12 at 20:25
  • Why do you care about "order" if this is an associative array? – Mike Brant Jun 26 '14 at 17:04

8 Answers8

30

Tested and works :)

function replace_key($array, $old_key, $new_key) {
    $keys = array_keys($array);
    if (false === $index = array_search($old_key, $keys, true)) {
        throw new Exception(sprintf('Key "%s" does not exist', $old_key));
    }
    $keys[$index] = $new_key;
    return array_combine($keys, array_values($array));
}

$array = [ 'a' => '1', 'b' => '2', 'c' => '3' ];    
$new_array = replace_key($array, 'b', 'e');
Kristian
  • 3,283
  • 3
  • 28
  • 52
12

Something like this may also work:

$langs = array("EN" => "English", 
        "ZH" => "Chinese", 
        "DA" => "Danish",
        "NL" => "Dutch", 
        "FI" => "Finnish", 
        "FR" => "French",
        "DE" => "German");
$json = str_replace('"EN":', '"en":', json_encode($langs));
print_r(json_decode($json, true));

OUTPUT:

Array
(
    [en] => English
    [ZH] => Chinese
    [DA] => Danish
    [NL] => Dutch
    [FI] => Finnish
    [FR] => French
    [DE] => German
)
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 8
    I personally find this solution very elegant! +1 – Luca Borrione May 08 '12 at 15:01
  • 3
    Me too. This works wonderful for removing dashes/underscores from XML tag names when using Zend_Config_XML::toArray(). In my case, the data is options for select elements w/ optgroups. My optgroup names were XML tags, and the titles had dashes. This worked well for that. – axiom82 Jun 24 '14 at 16:59
  • 5
    I dont find this elegant. Very dangerous. – nawfal Nov 12 '15 at 10:28
  • 1
    This only works because the example data is a very specific case. If the key to be replaced exists as any value then that would also be replaced. This is not a safe solution. – Phil Hilton Oct 10 '18 at 00:07
  • 1
    @PhilHilton: No where I mentioned this to be a panacea kind of solution. This is just a shorthand work around. Moreover having `"EN":` (quoted string followed by colon) in value is very rare. – anubhava Oct 12 '18 at 04:41
8

One way would be to simply use a foreach iterating over the array and copying it to a new array, changing the key conditionally while iterating, e.g. if $key === 'foo' then dont use foo but bar:

function array_key_rename($array, $oldKey, $newKey) 
{
    $newArray = [];
    foreach ($array as $key => $value) {
        $newArray[$key === $oldKey ? $newKey : $key] = $value;
    }
    return $newArray;
}

Another way would be to serialize the array, str_replace the serialized key and then unserialize back into an array again. That isnt particular elegant though and likely error prone, especially when you dont only have scalars or multidimensional arrays.

A third way - my favorite - would be you writing array_key_rename in C and proposing it for the PHP core ;)

NikiC
  • 100,734
  • 37
  • 191
  • 225
Gordon
  • 312,688
  • 75
  • 539
  • 559
1

Check keys existence before proceeding… Otherwise the result can be catastrophic if the new key already exists... or unnecessary processing time/memory consumption if the key to be replaced does not exist.

function array_rename_key( array $array, $old_key, $new_key ) {
    // if $new_key already exists, or if $old_key doesn't exists
    if ( array_key_exists( $new_key, $array ) || ! array_key_exists( $old_key, $array ) ) {
        return $array;
    }

    $new_array = [];
    foreach ( $array as $k => $v ) {
        $new_array[ $k === $old_key ? $new_key : $k ] = $v;
    }

    return $new_array;
}
ReskatoR
  • 11
  • 2
0

We are using this function for changing multiple array keys within an array keeping the order:

function replace_keys($array, $keys_map) {
    $keys = array_keys($array);
    foreach($keys_map as $old_key=>$new_key){
        if (false === $index = array_search($old_key, $keys)) {
            continue;
        }
        $keys[$index] = $new_key;
    }
    return array_combine($keys, array_values($array));
}

You can pass an array as $keys_map, like this:

$keys_map=array("old_key_1"=>"new_key_1", "old_key_2"=>"new_key_2",...)

This solution is based on Kristian one.

0

If possible, one can also put the key to change at the end of the array in the moment of creation :

$array=array('key1'=>'value1','key2'=>'value2','keytochange'=>'value');
$x=$array['keytochange'];
unset($array['keytochange']);
$array['newkey']=$x;
abb
  • 1
  • 1
0

Do a double flip! At least that is all I can think of:

$array=("foo"=>"bar","asdf"=>"fdsa");
$array=array_flip($array);
$array["bar"]=>'newkey';
$array=array_flip($array);
Tim Withers
  • 12,072
  • 5
  • 43
  • 67
-2

You could use array_combine. It merges an array for keys and another for values...

For instance:

$original_array =('foo'=>'bar','asdf'=>'fdsa');
$new_keys       = array('abc', 'def');
$new_array      = array_combine($new_keys, $original_array);
Gianluca78
  • 794
  • 1
  • 9
  • 24
  • what if you have many many keys and want to change just one? With your solution you would be obliged to write an endless $new_keys array. – Luca Borrione May 08 '12 at 14:58