Populating a PHP array: check for index first? - php

If I'm deep in a nest of loops I'm wondering which of these is more efficient:
if (!isset($array[$key])) $array[$key] = $val;
or
$array[$key] = $val;
The second form is much more desirable as far as readable code goes. In reality the names are longer and the array is multidimensional. So the first form ends up looking pretty gnarly in my program.
But I'm wondering if the second form might be slower. Since the code is in one of the most frequently-executed functions in the program, I'd like to use the faster form.
Generally speaking this code will execute many times with the same value of "$key". So in most cases $array[$key] will already be set, and the isset() will return FALSE.
To clarify for those who fear that I'm treating non-identical code as if it were identical: as far as this part of the program is concerned, $val is a constant. It isn't known until run-time, but it's set earlier in the program and doesn't change here. So both forms produce the same result. And this is the most convenient place to get at $val.

For an array you actually want: array_key_exists($key, $array) instead of isset($array[$key]).

isset() is very fast with ordinary variables, but you have an array here. The hash-map algorithm for arrays is quick, but it's still takes more time than doing nothing.
Now, first form can be faster if you have more values that are set, than those that are not, simply because it just looks up for hash without fetching or setting the value. So, that could be a point of difference: pick the first form if you have more 'hits' at keys that are set, and pick the second one if you have more 'misses'.
Please note that those two pieces of code are not identical. The first form will not set the value for some key when it's already set - it prevents 'overwriting'.

Have you measured how often you run into the situation that $array[$key] is set before you try to set it? I think one cannot give a general advice on this, because if there are actually a lot of those cases, the isset check could possibly save some time by avoiding unnessecary sets on the array. However, if this is just rarely the case, the overhead could slow you down …. The best thing would be to do a benchmark on your actual code.
However, be aware that both codes can lead to different results! If $val is not always the same for a $array[$key] combination, the former code would always set the value to the first $val for that $array[$key] where the latter code would always set it to the last value of that combination.
(I guess you are aware of that and $val is always the same for $array[$key], but some reader stopping by might not.)

The overhead of a comparison which may or may not be true seems like it should take longer.
What does running the script in both configurations show for performance time?

You should check the array upto but not including the level you are going to set.
If you're going to set
$anArray[ 'level1' ][ 'level2' ][ 'level3' ] = ...
You should make sure that the path upto level2 actually exists prior to setting level3.
$anArray[ 'level1' ][ 'level2' ]
No puppies will actually be killed if you don't, but they might be annoyed depending on your particular environment.
You don't have to check the index you are actually setting, because setting it automatically means it is declared, but in the interest of good practice you should make sure nothing is magically created.
There is an easy way to do this:
<?php
function create_array_path( $path, & $inArray )
{
if ( ! is_array( $inArray ) )
{
throw new Exception( 'The second argument is not an array!' );
}
$traversed = array();
$current = &$inArray;
foreach( $path as $subpath )
{
$traversed[] = $subpath;
if ( ! is_array( $current ) )
{
$current = array();
}
if ( ! array_key_exists( $subpath, $current ) )
{
$current[ $subpath ] = '';
}
$current = &$current[ $subpath ];
}
}
$myArray = array();
create_array_path( array( 'level1', 'level2', 'level3' ), $myArray );
print_r( $myArray );
?>
This will output:
Array
(
[level1] => Array
(
[level2] => Array
(
[level3] =>
)
)
)

The extra function call to isset() is almost guaranteed to have more overhead than any assignment. I would be extremely surprised if the second form is not faster.

Do you need an actual check to see if the key is there? With an assignment to a blank array the isset() will just slow the loop down. And unless you do a second pass with data manipulation I strongly advise against the isset check. This is population, not manipulation.

You can take a look at the PHP source code to see the difference. Didn't check whether this would be different in later versions of PHP, but it would seem in PHP3 the associative array functionality is in php3/php3_hash.c.
In the function _php3_hash_exists, the following things are done:
key is hashed
correct bucket found
bucket walked, until correct item found or not
Function _php3_hash_add_or_update:
hashed
bucket found
walked, existing overridden if existed
if didn't exist, new one added
Therefore it would seem just setting it is faster, because there is just one function call and this hashing and bucket finding business will only get done once.

i am a newbie to PHP but a combination of both could be with the ternary operator
$array[$key] = !isset($array[$key]) ? $val : $array[$key];
that's one way to go with it.

Related

PHP: return length of each array Element or stop

given I have an array, say:
$myArray=['12','AB','3C']
I want to return the value 2 (which is the length of each of the array elements indivudually.)
But in case I have something like
$myArray=['12','AB2','3C']
I want to stop the calculation/loop right after the second element of the array 'AB2' and let my function return null.
What is the most effective way to reach this in the matter of being performance and speed effective? Since such an array can get long.
Casual way
I think you are trying to stop the array loop the moment you get two different lengths in an element?
In that case, at worst, you'd need an O(n) runtime (since you need to verify every element, unless you have an abstract data type in mind in which case it could be O(1), if it is stored in the object property or you calculate the difference detected on the fly while pushing items into arrays)
Since the moment we discover an element is not the same length, we can simply quickly store the length of the first element in the array since we know if we detect any other length other than what we stored, we can immediately return null
function linear_loop($array) {
$len_of_first = strlen($array[0]);
foreach ($array as $val) {
if (strlen($val) != $len_of_first) {
return null;
}
}
//Function still running, entire array was same, return the length of first element
return $len_of_first;
}
This function is O(n) with each operation is constant. strlen is O(1)
Algorithmic complexity of PHP function strlen()
Most "performance-fastest"
Since you said that the array can get quite long, if you are not immediately generating the array, but rather you need to push items into it, then in your push operation, you can check before pushing it the item_to_be_pushed is the same strlen or whatever property you are trying to compare as the one you've stored (which can be picked arbitrarily, since the array must be of uniform some property)
In this case, you could have some object with property: uniform_length and store that. Then whenever you push into your array, you can check against it with the uniform_length. If it isn't the same length, then you can store in the object property called uniform as false. (By default uniform is true since if there is only one element in the array, it must be uniform).
This would be an O(1) calculation since it is stored as an attribute. But you probably don't need an object for something as simple as this, and you can just store it as some variable.
O(1) vs O(n) Runtime and why it is more performance effective
Since not everyone knows Big O, a quick explanation on what I said. O(1) runtime is "infinitely" better than O(n) runtime since the runtime of the function will not grow with input (as in processing 1 million items require the same amount of steps as processing 1 item)
Just loop through and return early when you find something that isn't correct. Don't worry about micro-optimizations until you have profiled and found that this function is really your bottleneck
ex.
function isCorrect($arr) {
$len = strlen($arr[0]);
for($arr as $val) {
if(strlen($val) != $len) {
return false;
}
}
return true;
}
Just my two cents. You could also use array_map for this:
$myArray = ['12','AB','3CC'];
$lengths = array_map('strlen', $myArray);
// output
Array
(
[0] => 2
[1] => 2
[2] => 3
)
you can just write an if statement and check the max($lengths) and return true or false
-Cheers

foreach( ... ) key on only - is array_keys(...) the most efficient?

if I have some code like:
foreach(array_keys($array) as $key)
{
// work with the array
}
I have 2 questions
Is array_keys( ... ) called for every single iteration of foreach( ... )? Is it efficient?
If I was to compare speed and memory, is the code above faster/more efficient than
foreach($array as $key => $data )
{
// use $key only
}
1.) If array_keys($array) would be evaluated in every iteration, then i don't think, the position where you were last time could be remembered, since array_keys() would return a fresh array with keys every time, plus it would be really bad in terms of performance, so no. This goes for all expressions which evaluates as some form of iterable, and goes in the first part of a foreach.
2.) If you iterate on the whole array, then here is what happens:
a) array_keys() first extracts all the keys from your array, which kind of a mix of a List and a Hashmap (if you are familiar with java or some strongly typed oo language), and it might be really fast or really slow depending on the implementation of the arrays internal structure and the array_keys method. Then if you iterate on the array, you need to do a lookup in every iteration, like $value = $array[$currentlyIteratedKey]; which also needs some time, especially with string keys.
b) The foreach loop is a language construct (probably better optimized), and there is no additional lookup, you get the key and the value in every iteration, so i think it would be faster.
Hope it helps, correct me if I'm wrong!
1 - faster when use all array
2 - faster when need to "find" some key and break

Perform operations after extract()

I have been looking for an answer, but maybe because I am searching the wrong terms or concepts - I did not yet find any lead on how to perform operations on extracted arguments .
I have an array, which is dynamic ( I can not predict how many $vars or of what name or type )
global $o99_option;
extract( $o99_option );
so now, theoretically, I can have any number of $vars with any possible name..
example :
$first_var = 'first-var';
$my_second_var = 'another';
$enigma = NULL;
$answer = '42';
my question is - how can I perform operations on the result of extract ( after extraction ) in a manner that will effect all the created $vars ?
for example, let´s say I want to trim all of those variables - How do I achieve that ?
( that , In my twisted mind, should have the same effect as
foreach (extract( $o99_option ) as $single_var ){
do_something($single_var);
}
)
Edit I : So, is there a way to perform operations on all extracted elements ? ( assuming I DO need to extract them ..) Or should I always do that BEFORE the extraction ( for example with the help of array_map() ) - or, should I , like people here suggested just forget that extract ever existed never ever use it..
Edit II :
Sure, like many here said , I can also do
$first_var = $o99_option['first-var'];
$my_second_var = $o99_option['my_second_var'];
$enigma = $o99_option[enigma];
$answer = $o99_option['answer'];
but
1) it seems a little absurd doing that for 100+ variables, and
2 ) what to do with the ones that I need to use their value as name ?
right now, for example, I use
${$title_field} = $formdata[$post_title_field];
where I know that $title_field exists as $o99_option['title_field'], and therefore exists as a variable after extract ..
Again, I might be approaching that all wrong, but up until now the script works great , and actually , the first comment by #Mark Baker ( using array_map() ) might be the best option when doing so BEFORE the extract ...
You must not extract the variables, you need to keep them in the $o99_option array otherwise it's impossible to determine the number of elements.
foreach ($o99_option as $variable) {
// Do something with the variable
}
Imagine you're sitting in one of those kids ball pits. This is your variable scope. You have all sorts of variables floating around in this scope. Now you get handed a box full of even more colourful balls. Those balls are important balls, they're more interesting to you than all the other balls around you.
What you're doing with extract is you're turning the box upside down and empty all those balls into the ball pit and mix them with all the other balls. And you didn't even really know what balls were in that box exactly, but you emptied the box anyway. Well, good luck finding those balls again now and doing anything with them.
Keep the balls in the box! While they're in the box, you can inspect them one by one. You can look at each one in turn and do something with it. You can carry them all around inside that box together. Don't extract!
If you know what you're looking for in that box:
echo $o99_option['first-var'];
Simple. Just take that ball directly and do whatever you want with it.
If you want to check whether a specific ball is in the box:
if (isset($o99_option['first-var']))
If you want to look through all balls and do something with each of them:
foreach ($o99_option as $name => $ball) {
echo "$name: $ball";
}
If you want to do something to all the variables:
foreach ($o99_option as &$variable) {
$variable = strtoupper($variable); //for example
}
Then you should work with each element individually. Don't extract.

How do I clear the values in a PHP array while maintaining its keys?

I would like to take an array and clear all of its values while maintaining its keys. By "clear" I mean replace with an empty type value, such as '' or null. PHP obviously has quite a few array functions, but I didn't find anything there that does exactly this. I am currently doing it this way:
foreach ($array as &$value) $value = ''
My question is, is there some built-in php function I overlooked, or any other way I can accomplish this without iterating over the array at this level?
Without knowing exactly what your memory/performance/object-management needs are, it's hard to say what's best. Here are some "I just want something short" alternatives:
$array = array_fill_keys(array_keys($a),""); // Simple, right?
$array = array_map(function(){return "";},$a); // More flexible but easier to typo
If you have an array that's being passed around by reference and really want to wipe it, direct iteration is probably your best bet.
foreach($a as $k => $v){
$a[$k] = "";
}
Iteration with references:
/* This variation is a little more dangerous, because $v will linger around
* and can cause bizarre bugs if you reuse the same variable name later on,
* so make sure you unset() it when you're done.
*/
foreach($a as $k => &$v){
$v = "";
}
unset($v);
If you have a performance need, I suggest you benchmark these yourself with appropriately-sized arrays and PHP versions.
Easiest way is array_map
$array = array_map(function($x) { return '';}, $array);
You can use array_keys to get the keys. You can then use array_flip if you like, although this will assign the values 0 through length-1 to the keys.
There is no single built-in function for this. You might try:
array_combine(array_keys($array),array_fill(0,count($array)-1,""));
But really the code you have right now does the job just fine.

Help loading contstants stored in serialized array using eval() and constant()

DISCLAIMER:
Please read carefully as this is NOT a question about storing arrays in constants or simple eval() or serialize() techniques. This IS a question primarily about how constants work in PHP and why the constant() function is not working to convert a constant name into a constant value. Thanks.
BACKGROUND:
For various reasons, I started out with a flat config file for a homebrewed LAMP(PHP) CMS (in private development). Looking back this may have been misguided, and I have transitioned my variable storage into a DB table. However, the bulk of the code still depends on the CONSTs, so I use eval("define(A, B...);") to load the DB values A and B into constants. This part works fine. But it gets a bit more complicated.
PROBLEM:
The problem I'm having now is with constants in arrays (NB. NOT arrays in constants). I have a big, GLOBAL array called defaults that contains config settings in the format shown below.
Initially, I declare: <?php define('THIS_IS_A_CONSTANT', 'abcdefg'); ?> (And THIS WORKS...)
Next, I define $GLOBALS['defaults'] as the following nested array:
Array
(
'var_name' => Array
(
'display' => THIS_IS_A_CONSTANT,
'value' => 12,
'type' => 'int',
'params' => Array ( ... )
),
...
Lots more variables...
...
)
To prevent the client (who has file system access but no direct DB access but can change certain values, including most constants, via the CMS's administrative backend) from mucking up this array structure, I serialize the array structure and store that string in the DB. On each page request, I first define all the constants (stored in the DB) using the eval(define(A,B...)), then I unserialize the array above (which was serialized and stored in the DB). However, no matter what I try I cannot get the values at $GLOBALS['defaults']['var_name']['display'] to be recognized as the values that the constants contain. Instead, the constant name shows up and NOT the constant value (in other words, my output contains THIS_IS_A_CONSTANT instead of 'abcdefg'). Very frustrating, right?
I've tried something like the following (where $arr contains the unserialized array that I fetch from the DB):
foreach ($arr as $item => $contents) {
$display = isset($contents['display']) ? $contents['display'] : 1;
$value = constant("$display");
// This doesn't work, though it seems like it should
$contents['display'] = $value;
// Neither does this, no matter how much I juggle the quotation marks and backslashes
eval("\$contents['display'] = constant(\"$value\");");
// or this ...
eval("\$contents['display'] = $value;");
// or this ...
eval("\$contents['display'] = \$value;");
// or a number of other things...
}
$GLOBALS['defaults'] = $arr;
QUESTIONS:
Has anyone dealt with this kind of situation before? Can anyone advise me how to force my constants to be recognized as CONSTANTS and not strings. Do I need to serialize my array differently? Or maybe process the unserialized array differently (after retrieving it from the DB)? Is these some combination of eval() and constant() that will allow me to do this? Why are the constants within my array behaving badly while the constants I define normally are working without problem? Any help at all would be greatly appreciated, as I've been puzzling over this for a few days now and haven't come to any solutions.
All the best, Dakota.
Although eval does have its uses, this is not one of those cases. You have no idea what is being submitted to the eval at run time - and by the sounds of things you are storing something which you are then treating as code in a user-data storage location.
If the constant was defined before the array was declared then the serialized version would contain the value instead of the label. I can only assume that the value may differ depending on the context at run-time.
I would suggest that a better solution would be to roll your own macro language in PHP, e.g. something like:
<?php
global $defs;
$defs=array(
'THIS_IS_A_CONSTANT' => 'abcdefg',
'SO_IS_THIS' => 23
);
// for inline constants
foreach ($defs as $label =>$val) {
define($label, $key);
}
replacer($defs,true);
function replacer($in, $init=false;)
{
static $defs;
static $vals;
if ($init) {
$defs=array_keys($in);
$vals=array_values($in);
return count($in);
}
return str_replace($defs, $vals, $in);
// you might want to use preg_replace() with a pattern based on $defs for a neater solution
}
function fix_var(&$in)
{
if (is_array($in)) {
foreach ($in as $key=>$dummy) {
fix_var($in[$key]);
}
} else {
$in=replacer($in);
}
}
?>
C.
First, why are you evaling? From what you are saying you want the value of $GLOBALS['defaults']['var_name']['display'] to be the value of the constant THIS_IS_A_CONSTANT. You have de-serialized the string from your database and shoved in in $GLOBALS['defaults'] and the string stored the value as the constant name, not the value of the constant.
Have you tried:
<?php
define('THIS_IS_A_CONSTANT', 'abcdefg');
$value = constant($GLOBALS['defaults']['var_name']['display']);
//$value should now be abcdefg not THIS_IS_A_CONSTANT
$GLOBALS['defaults']['var_name']['display'] = $value;
?>
If "$GLOBALS['defaults']['var_name']['display']" does contain the string THIS_IS_A_CONSTANT then all you should need to do is pass that string to the constant funct.
Am I missing something?

Categories