in a very tight loop I need to access tenthousands of values in an array containing millions of elements. The key can be undefinied: In that case it shall be legal to return
I prefer using the isset
function instead of escaping the error.
I made a function to check the key exists and if not returns a default value, in the case of nested arrays you just need to add the other keys in order:
Nested array lookup:
/**
* Lookup array value.
*
* @param array $array
* @param array $keys
* @param $defaultValue
*/
public static function array_key_lookup($array, $keys, $defaultValue)
{
$value = $array;
foreach ($keys as $key) {
if (isset($value[$key])) {
$value = $value[$key];
} else {
$value = $defaultValue;
break;
}
}
return $value;
}
Usage example:
$array = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => [
'key3a' => 'value3a',
'key3b' => 'value3b'
]
];
array_key_lookup($array, ['key3', 'key3a'], 'default')
'value3a'
array_key_lookup($array, ['key2', 'key2a'], 'default')
'default'
array_key_lookup($array, ['key2'], 'default')
'value2'
array_key_lookup($array, ['key5'], 'default')
'default'
Escaping the error:
$value = @$array[$key1][$key2] ?: $defaultValue;
Just a sudden idea that would have to be tested, but did you try using array_intersect_key()
to get the existing values and a array_merge to fill() the rest ? It would remove the need of a loop to access the data. Something like that :
$searched_keys = array ('key1' => null, 'key2' => null); // the list of the keys to find
$exiting_values = array_intersect_key($lookup_table, $searched_keys);
$all_values = array_merge($searched_keys, $exiting_keys);
Please note that I did not tried it performance-wise.
There are two typical approaches to this.
Here is how to perform the first and as little code as possible.
$data = array_merge(array($key=>false),$data);
return $data[$key];
Here is how to perform the second.
return isset($data[$key]) ? $data[$key] : false;
The @ operator and the error_reporting methods will both be slower than using isset. With both of these methods, it modifies the error reporting setting for PHP, but PHP's error handler will still be called. The error handler will check against the error_reporting setting and exit without reporting anything, however this has still taken time.
Since PHP 7 you can accomplish this with the null coalesce operator:
return $table[$key] ?? null;
First of all, arrays are not implemented as a B-tree, it's a hash table; an array of buckets (indexed via a hash function), each with a linked list of actual values (in case of hash collisions). This means that lookup times depend on how well the hash function has "spread" the values across the buckets, i.e. the number of hash collisions is an important factor.
Technically, this statement is the most correct:
return array_key_exists($key, $table) ? $table[$key] : null;
This introduces a function call and is therefore much slower than the optimized isset()
. How much? ~2e3 times slower.
Next up is using a reference to avoid the second lookup:
$tmp = &$lookup_table[$key];
return isset($tmp) ? $tmp : null;
Unfortunately, this modifies the original $lookup_table
array if the item does not exist, because references are always made valid by PHP.
That leaves the following method, which is much like your own:
return isset($lookup_table[$key]) ? $lookup_table[$key] : null;
Besides not having the side effect of references, it's also faster in runtime, even when performing the lookup twice.
You could look into dividing your arrays into smaller pieces as one way to mitigate long lookup times.
This Work for me
{{ isset($array['key']) ? $array['key']: 'Default' }}
but this is fast
{{ $array['key'] or 'Default' }}