Reputation: 10311
I am currently optimizing a PHP application and found one function being called around 10-20k times, so I'd thought I'd start optimization there:
function keysToLower($obj)
{
if (!is_object($obj) && !is_array($obj))
return $obj;
foreach ($obj as $key => $element) {
$element = keysToLower($element);
if (is_object($obj)) {
$obj->{strtolower($key)} = $element;
if (!ctype_lower($key))
unset($obj->{$key});
} elseif (is_array($obj) && ctype_upper($key)) {
$obj[strtolower($key)] = $element;
unset($obj[$key]);
}
}
return $obj;
}
Most of the time is spent in recursive calls (which are quite slow in PHP), but I don't see any way to convert it to a loop. How can I do this?
Upvotes: 3
Views: 5389
Reputation: 16620
You might also want to lookup array_change_key_case().
For objects, you can do:
($obj)array_change_key_case((arr)$o)
Upvotes: 3
Reputation: 48041
Here is a recursive function which modifies by reference to replace all keys/properties in an array or object structure that may contain any nested arrays or objects. It makes a copy of each level and overwrites the level after it is finished changing all keys.
Code: (Demo)
function allKeysToLower(array|object &$data): void
{
$type = gettype($data);
foreach ($data as $k => &$v) {
if (is_array($v) || is_object($v)) {
(__FUNCTION__)($v); // go to deeper level
}
if (is_string($k)) {
$k = strtolower($k); // mutate the key
}
switch ($type) {
case 'object':
$new ??= (object) []; // create object if not created, to allow population
$new->{$k} = $v; // add property to object
break;
case 'array':
$new[$k] = $v; // add element to array
}
}
$data = $new ?? $data; // fallback to empty $data if loop not entered
}
allKeysToLower($test);
var_export($test);
Upvotes: 0
Reputation: 1
A some what late response to a old thread but, there's a native function that does this, you could wrap it up something along these lines.
function setKeyCasing($thing, $case = CASE_LOWER) {
return array_change_key_case((array) $thing, $case);
}
Upvotes: 0
Reputation: 8187
here a example using lambda:
$multiArrayChangeKeyCase = function (&$array) use (&$multiArrayChangeKeyCase) {
$array = array_change_key_case($array);
foreach ($array as $key => $row)
if (is_array($row))
$multiArrayChangeKeyCase($array[$key]);
};
Upvotes: 2
Reputation: 257
I assume you don't care about casting to array...
function keys_to_lower($o) {
if (is_object($o)) {
$o = (array)$o;
}
if (is_array($o)) {
return array_map('keys_to_lower', array_change_key_case($o));
}
else {
return $o;
}
}
Upvotes: 2
Reputation: 655707
Foreach
is using an internal copy that is then traversed. Try it without:
function keysToLower($obj)
{
$type = (int) is_object($obj) - (int) is_array($obj);
if ($type === 0) return $obj;
reset($obj);
while (($key = key($obj)) !== null)
{
$element = keysToLower(current($obj));
switch ($type)
{
case 1:
if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
{
unset($obj->{$key});
$key = $keyLowercase;
}
$obj->{$key} = $element;
break;
case -1:
if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
{
unset($obj[$key]);
$key = $keyLowercase;
}
$obj[$key] = $element;
break;
}
next($obj);
}
return $obj;
}
Or use references to avoid that a copy is used:
function &keysToLower(&$obj)
{
$type = (int) is_object($obj) - (int) is_array($obj);
if ($type === 0) return $obj;
foreach ($obj as $key => &$val)
{
$element = keysToLower($val);
switch ($type)
{
case 1:
if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
{
unset($obj->{$key});
$key = $keyLowercase;
}
$obj->{$key} = $element;
break;
case -1:
if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
{
unset($obj[$key]);
$key = $keyLowercase;
}
$obj[$key] = $element;
break;
}
}
return $obj;
}
Upvotes: 5
Reputation: 145512
array_combine(array_map("strtolower", array_keys($a)), array_values($a))
Upvotes: 1