19

How i can do this:

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

I didn't see a function that does that. There is a way to do this?

Cristian Gonzales
  • 273
  • 1
  • 3
  • 10

5 Answers5

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;
}
zerkms
  • 249,484
  • 69
  • 436
  • 539
  • Quite interesting approach, in my opinion it would be better to pass array as reference. – Nazariy Apr 16 '12 at 22:45
  • 3
    @Nazariy: php wouldn't copy original parameter until it is modified (that's called copy-on-write, COW). From this point of view I don't see any advantages of using references in this particular case – zerkms Apr 16 '12 at 22:47
  • Thanks zerkms, a elegant approach. – Cristian Gonzales Apr 16 '12 at 22:50
  • @zerkms just wondering, i have an array where the first 2 index's are strings, the 3rd is an integer and the rest after it are strings, would your function still work? – Memor-X Jan 17 '13 at 01:36
  • 3
    @Memor-X: so instead of trying you're assuming I will do that for you while you're watching youtube videos with kittens? – zerkms Jan 17 '13 at 01:40
  • @zerkms no, its just that when i use it the index becomes 0 so i'm wondering if it's just your code only works with string index's or if there's something else going on in mine, i'm working off Magento so there could be god know what messing with the code at any given point – Memor-X Jan 17 '13 at 01:43
  • @Memor-X: create a new question with your current code and current data explaind. And refer me to there – zerkms Jan 17 '13 at 01:55
  • I also ran into the problem with indexes mixed with named keys. I posted an answer that addresses the issue. – Dieter Gribnitz Jul 01 '14 at 14:51
  • @DieterGribnitz You just need to pass `true` as the third argument to `array_search()`; done and dusted :) – Ja͢ck Jul 01 '14 at 16:18
  • I tested and verified the concern that Dieter raised, and arrived at the same conclusions a @Jack... need to set the 'strict' flag to 'true'. – CragMonkey Dec 01 '14 at 00:16
4

There is a flaw in the logic of the accepted answer.

If you have an array like this:

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

and replace 'k4' with 'something' you will get an output like this:

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

Here is a quick fix that solves the problem:

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 The accepted answer does work if you set the third parameter (strict) of array_search to true.

Dieter Gribnitz
  • 5,062
  • 2
  • 41
  • 38
  • 1
    Correct, but simply setting the "strict" flag to true in array_keys will get you the desired output and save you 8 lines of code. Changing $index = array_search($key1, $keys); to $index = array_search($key1, $keys, true); in the above function generates the correct result: [ 'k1'=>'k1', 'k2'=>'k2', 0 =>'k3', 'something'=>'k4' ] – CragMonkey Dec 01 '14 at 00:06
  • 2
    Thanks, you are correct. I also figured this out eventually but forgot about this post. Will update it now. – Dieter Gribnitz Dec 03 '14 at 14:51
  • Just wanted to thank you for this ... Appreciated! – salih0vicX Sep 01 '16 at 11:58
3

Instead of using loops, you could always flatten to string with json_encode(), perform a string replacement, then json_decode() back to an array:

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

Now this doesn't check for conflicts with pre-existing keys (easy enough to add a string comparison check), and it might not be the best solution for single replacements in massive arrays.. but the nice part about flattening the array into a string for replacement is that it effectively makes replacement recursive since matches at any depth are all replaced in one pass:

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

outputs

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
                        )
                )
        )
)
WebChemist
  • 4,393
  • 6
  • 28
  • 37
2

A generic and simple solution with PHP 5.3+ using 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;
}
klaus triendl
  • 1,237
  • 14
  • 25
1

a good answer has been posted, but here's my two pence:

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

in response to mike-purcell would this be a more accepted approach to my example above?

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

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

I'm always looking to improve :)

TerryProbert
  • 1,124
  • 2
  • 10
  • 28