Renaming the key AND keeping the ordering consistent (the later was important for the use case that the following code was written).
<?php
/**
* Rename a key and preserve the key ordering.
*
* An E_USER_WARNING is thrown if there is an problem.
*
* @param array &$data The data.
* @param string $oldKey The old key.
* @param string $newKey The new key.
* @param bool $ignoreMissing Don't raise an error if the $oldKey does not exist.
* @param bool $replaceExisting Don't raise an error if the $newKey already exists.
*
* @return bool True if the rename was successful or False if the old key cannot be found or the new key already exists.
*/
function renameKey(array &$data, $oldKey, $newKey, $ignoreMissing = false, $replaceExisting = false)
{
if (!empty($data)) {
if (!array_key_exists($oldKey, $data)) {
if ($ignoreMissing) {
return false;
}
return !trigger_error('Old key does not exist', E_USER_WARNING);
} else {
if (array_key_exists($newKey, $data)) {
if ($replaceExisting) {
unset($data[$newKey]);
} else {
return !trigger_error('New key already exists', E_USER_WARNING);
}
}
$keys = array_keys($data);
$keys[array_search($oldKey, array_map('strval', $keys))] = $newKey;
$data = array_combine($keys, $data);
return true;
}
}
return false;
}
And some unit tests (PHPUnit being used, but hopefully understandable as the purpose of the tests).
public function testRenameKey()
{
$newData = $this->data;
$this->assertTrue(Arrays::renameKey($newData, 200, 'TwoHundred'));
$this->assertEquals(
[
100 => $this->one,
'TwoHundred' => $this->two,
300 => $this->three,
],
$newData
);
}
public function testRenameKeyWithEmptyData()
{
$newData = [];
$this->assertFalse(Arrays::renameKey($newData, 'junk1', 'junk2'));
}
public function testRenameKeyWithExistingNewKey()
{
Arrays::renameKey($this->data, 200, 200);
$this->assertError('New key already exists', E_USER_WARNING);
}
public function testRenameKeyWithMissingOldKey()
{
Arrays::renameKey($this->data, 'Unknown', 'Unknown');
$this->assertError('Old key does not exist', E_USER_WARNING);
}
public function testRenameKeyWithMixedNumericAndStringIndicies()
{
$data = [
'nice', // Index 0
'car' => 'fast',
'none', // Index 1
];
$this->assertTrue(Arrays::renameKey($data, 'car', 2));
$this->assertEquals(
[
0 => 'nice',
2 => 'fast',
1 => 'none',
],
$data
);
}
The AssertError assertion is available for PHPUnit from https://github.com/digitickets/phpunit-errorhandler