I have an array that contains the location of a value in a very large multidimensional array. I need to take this location and replace the value at the location with another value. I have found numerous articles about returning the value of a position using such an array of indexes by writing a recursive function. However, this won't work because I can't slice up the large array, I need to replace just that one value.
The location would look something like:
array(1,5,3,4,6);
The code I had to find a value is the following:
function replace_value($indexes, $array, $replacement){
if(count($indexes) > 1)
return replace_value(array_slice($indexes, 1), $array[$indexes[0]], $replacement);
else
return $array[$indexes[0]];
}
}
How would I modify this to instead of recursively cutting down an array until the value is found I can simply modify a part of a large array? Is there a way to build
array(1,5,3,4,6);
Into
$array[1][5][3][4][6];
Thanks
You could modify your function like this:
function replace_value($indexes, &$array, $replacement){
if(count($indexes) > 1) {
return replace_value(array_slice($indexes, 1), $array[$indexes[0]], $replacement);
} else {
return $array[$indexes[0]] = $replacement;
}
}
Make sure your write &$array in the function definition, not $array This will pass in the actual array, so that you can modify it in place. Otherwise you would just be passing in a copy.
Assuming you trust the contents of the variable containing your array indices, this is a completely valid use of eval:
$keys = array(1,5,3,4,6);
$keys = "[" . join($keys, "][") . "]";
$value = "what";
eval("\$array$keys = '$value';"); # $array[1][5][3][4][6] = 'what';
Here's a solution without using eval. Go through each key and reduce the array as you go. The $ref variable below is a reference to the original array so changing it will change the original.
$keys = array(1,5,3,4,6);
$array[1][5][3][4][6] = 'foo';
$ref = &$array;
foreach( $keys as $key ) {
$ref = &$ref[ $key ];
}
$ref = 'bar';
echo $array[1][5][3][4][6]; // 'bar'
This is untested. I tend to shy away from using references because I think they're particularly confusing, and they leave remnant reference in your code that can cause difficult to find bugs.
$keys = array(1,5,3,4,6);
$path = 'new leaf value';
foreach (array_reverse($keys) as $key) {
$path = array($key => $path);
}
$modified = array_replace_recursive($origionalArray, $path);
Related
I'm trying to find a simpler way to create new arrays from existing arrays and values. There are two routines I'd like to optimize that are similar in construction. The form of the first one is:
$i = 0;
$new_array = array();
foreach ($my_array as $value) {
$new_array[$i][0] = $constant; // defined previously and unchanging
$new_array[$i][1] = $value; // different for each index of $my_array
$i++;
}
The form of the second one has not one but two different values per constant; notice that $value comes before $key in the indexing:
$i = 0;
$new_array = array();
foreach ($my_array as $key => $value) {
$new_array[$i][0] = $constant; // defined previously and unchanging
$new_array[$i][1] = $value; // different for each index of $my_array
$new_array[$i][2] = $key; // different for each index of $my_array
$i++;
}
Is there a way to optimize these procedures with shorter and more efficient routines using the array operators of PHP? (There are many, of course, and I can't find one that seems to fit the bill.)
I believe a combination of Wouter Thielen's suggestions regarding the other solutions actually holds the best answer for me.
For the first case I provided:
$new_array = array();
// $my_array is numeric, so $key will be index count:
foreach ($my_array as $key => $value) {
$new_array[$key] = array($constant, $value);
};
For the second case I provided:
// $my_array is associative, so $key will initially be a text index (or similar):
$new_array = array();
foreach ($my_array as $key => $value) {
$new_array[$key] = array($constant, $value, $key);
};
// This converts the indexes to consecutive integers starting with 0:
$new_array = array_values($new_array);
it is shorter, when you use the array-key instead of the $i-counter
$new_array = array();
foreach ($my_array as $key => $value) {
$new_array[$key][0] = $constant; // defined previously and unchanging
$new_array[$key][1] = $value; // different for each index of $my_array
}
Use array_map:
$new_array = array_map(function($v) use ($constant) {
return array($constant, $v);
}, $my_array);
If you want to use the keys too, for your second case:
$new_array = array_map(function($k, $v) use ($constant) {
return array($constant, $v, $k);
}, array_keys($my_array), $my_array);
Assuming the $constant variable is defined in the caller's scope, you'll need to use use ($constant) to pass it into the function's scope.
array_walk is similar, but modifies the array you pass to it, so if you want to update $my_array itself, use array_walk. Your second case then becomes this:
array_walk($my_array, function(&$val, $key) use($constant) {
$val = array($constant, $val, $key);
});
In both examples above for the second case, you'll end up with an associative array (i.e. with the keys still being the keys for the array). If you want to convert this into a numerically indexed array, use array_values:
$numerically_indexed = array_values($associative);
I asked a question similar to this a few days ago, check it out:
PHP - Fastest way to convert a 2d array into a 3d array that is grouped by a specific value
I think that you have an optimal way when it comes to dealing with large amount of data. For smaller amounts there is a better way as was suggested by the benchmarks in my question.
I think too that readability and understanding the code can also be an issue here and I find that things that you can understand are worth more later on than ideas that you do not really grasp as it generally takes a long time to understand them again as it can be quite confusing while debugging issues.
I would suggest, you take a look at the differences between JSON encoded arrays and serialised arrays as there can be major performance differences when working with the two. It seems that as it is now JSON encoded arrays are a more optimised format (faster) for holding and working with data however this will likely change with PHP 7. It would be useful to note that they are also more portable.
Further Reading:
Preferred method to store PHP arrays (json_encode vs serialize)
http://techblog.procurios.nl/k/n618/news/view/34972/14863/cache-a-large-array-json-serialize-or-var_export.html
I've splitted a string into array, giving a delimitator. So, this new array created, will contain values that I would want to use as indexes for another given array.
Having a situation like this:
// my given array
$array['key1']['key2']['a_given_key']['some_other_given_key'] = 'blablabl';
// the value of my given array
$value = $array['key1']['key2']['a_given_key']['some_other_given_key'];
$string = "key1;key2";
$keys = explode(";", $string);
I want to call dinamically (during the execution of my PHP script) the value of the given array, but, using as indexes all the values of the array $keys, and in addition appending the indexes ['a_given_key']['some_other_given_key'] of my given array.
I hope I have been clear.
Many thanks.
To make it work you have to use references. Below code should work as you expect:
<?php
$string = "key1;key2;key3;key4";
$keys = explode(";", $string);
$array['key1']['key2']['key3']['key4']['a_given_key']['some_other_given_key'] = 'blablabl';
$ref = & $array;
for ($i=0, $c = count($keys); $i<$c ; ++$i) {
$ref = &$ref[$keys[$i]];
}
echo $ref['a_given_key']['some_other_given_key'];
$value = $ref['a_given_key']['some_other_given_key'];
echo $value;
?>
I would like to add that just after using reference you should unset it using:
unset($ref);
If you don't do this and many lines later you run for example $ref = 2; it will modify your source array so you have to remember about unsetting references just after it's no longer in use.
I have a path like:
/blog/2/post/45/comment/24
Can I have an array depends on what I have on url, like :
$arr = array('blog'=>'2','post'=>'45','comment'=>'24');
But it should depend on variable passed:
/blog/2 should produce $arr = array('blog'=>'2');
Is this possible to create dynamic array?
You could try something like this:
function path2hash($path) {
// $path contains whatever you want to split
$chunks = explode('/', $path);
$result = array();
for ($i = 0; $i < sizeof($chunks) - 1; $i+=2)
$result[$chunks[$i]] = $chunks[$i+1];
return $result;
}
You could then use parse_url to extract the path, and this function to turn it into the desired hash.
First use $_SERVER['REQUEST_URI'] to find the current path.
now, you can use explode and other string functions to produce the array...
If you need a working example, Ill try and post one.
EDIT:
$path=explode('/',$path);
$arr=array(
$path[0]=>$path[1],
$path[1]=>$path[2]);
or don't know how long it is...
$arr=array();
for ($i=0; $i+1<count($path);i+=2)
$arr[$path[$i]]=$path[$i+1];
Here's a simple example trying to solve the issue. This will put the arguments in the "arguments" array, and will contain each combination of key/value in the array. If there's an odd number of arguments, the last element will be ignored.
This uses array_shift() to remove the first element from the array, which then is used as a key in the arguments array. We then remove the next element from the array, yet again using array_shift(). If we find an actual value here (array_shift returns NULL when the array is empty), we create a entry in the arguments array.
$path = '/blog/2/post/45/comment/24';
$elements = explode('/', $path);
// remove first, empty element
array_shift($elements);
$arguments = array();
while($key = array_shift($elements))
{
$value = array_shift($elements);
if ($value !== NULL)
{
$arguments[$key] = $value;
}
}
Not really an answer per se but you may find http://www.php.net/manual/en/function.parse-url.php useful.
What is the difference in the following array notations: $arr[$key] = $value and $arr[] = $value, which is a better way ?
function test(){
$key = 0;
$a = array(1,2,3,4,5);
foreach($a as $value){
$a[$key] = $value;
$key++;
}
print_r($a);
}
versus
function test(){
$a = array(1,2,3,4,5);
foreach($a as $value){
$a[] = $value;
}
print_r($a);
}
They are different.
$a[] = 'foo';
Adds an element to the end of the array, creating a new key for it (and increasing the overall size of the array). This is the same as array_push($array, 'foo');
$key = 0;
$a[$key] = 'foo';
Sets the 0 element of the array to foo, it overwrites the value in that location... The overall size of the array stays the same... This is the same as $array = array_slice($array, 0, 1, 'foo'); (but don't use that syntax)...
In your specific case, they are doing 2 different things. The first test function will result in an array array(1,2,3,4,5), whereas the second one will result in array(1,2,3,4,5,1,2,3,4,5). [] always adds new elemements to the end.... [$key] always sets....
$arr[$key] = $value sets a specific key to a specific value.
$arr[] = $value adds a value on to the end of the array.
Neither is "better". They serve completely different roles. This is like comparing writing and drawing. You use a pen for both of them, but which you use depends on the circumstance.
One ($a[$key] = $value) youare specifying the $key where PHP will override that array entry if you use the same key twice.
The other ($a[] = $value) PHP is handling the keys itself and just adding the array entry using the next available key.
The whole thing that you are doing is a bit redundant though, as in the first example you are trying to loop through the array setting its values to itself. In the second example you are duplicating the array.
If you want to append an element to the array I would use
$arr[] = $value;
It is simpler and you get all the information on that line and don't have to know what $key is.
If I had an array like:
$array['foo'] = 400;
$array['bar'] = 'xyz';
And I wanted to get the first item out of that array without knowing the key for it, how would I do that? Is there a function for this?
reset() gives you the first value of the array if you have an element inside the array:
$value = reset($array);
It also gives you FALSE in case the array is empty.
PHP < 7.3
If you don't know enough about the array (you're not sure whether the first key is foo or bar) then the array might well also be, maybe, empty.
So it would be best to check, especially if there is the chance that the returned value might be the boolean FALSE:
$value = empty($arr) ? $default : reset($arr);
The above code uses reset and so has side effects (it resets the internal pointer of the array), so you might prefer using array_slice to quickly access a copy of the first element of the array:
$value = $default;
foreach(array_slice($arr, 0, 1) as $value);
Assuming you want to get both the key and the value separately, you need to add the fourth parameter to array_slice:
foreach(array_slice($arr, 0, 1, true) as $key => $value);
To get the first item as a pair (key => value):
$item = array_slice($arr, 0, 1, true);
Simple modification to get the last item, key and value separately:
foreach(array_slice($arr, -1, 1, true) as $key => $value);
performance
If the array is not really big, you don't actually need array_slice and can rather get a copy of the whole keys array, then get the first item:
$key = count($arr) ? array_keys($arr)[0] : null;
If you have a very big array, though, the call to array_keys will require significant time and memory more than array_slice (both functions walk the array, but the latter terminates as soon as it has gathered the required number of items - which is one).
A notable exception is when you have the first key which points to a very large and convoluted object. In that case array_slice will duplicate that first large object, while array_keys will only grab the keys.
PHP 7.3+
PHP 7.3 onwards implements array_key_first() as well as array_key_last(). These are explicitly provided to access first and last keys efficiently without resetting the array's internal state as a side effect.
So since PHP 7.3 the first value of $array may be accessed with
$array[array_key_first($array)];
You still had better check that the array is not empty though, or you will get an error:
$firstKey = array_key_first($array);
if (null === $firstKey) {
$value = "Array is empty"; // An error should be handled here
} else {
$value = $array[$firstKey];
}
Fake loop that breaks on the first iteration:
$key = $value = NULL;
foreach ($array as $key => $value) {
break;
}
echo "$key = $value\n";
Or use each() (warning: deprecated as of PHP 7.2.0):
reset($array);
list($key, $value) = each($array);
echo "$key = $value\n";
There's a few options. array_shift() will return the first element, but it will also remove the first element from the array.
$first = array_shift($array);
current() will return the value of the array that its internal memory pointer is pointing to, which is the first element by default.
$first = current($array);
If you want to make sure that it is pointing to the first element, you can always use reset().
reset($array);
$first = current($array);
another easy and simple way to do it use array_values
array_values($array)[0]
Just so that we have some other options: reset($arr); good enough if you're not trying to keep the array pointer in place, and with very large arrays it incurs an minimal amount of overhead. That said, there are some problems with it:
$arr = array(1,2);
current($arr); // 1
next($arr); // 2
current($arr); // 2
reset($arr); // 1
current($arr); // 1 !This was 2 before! We've changed the array's pointer.
The way to do this without changing the pointer:
$arr[reset(array_keys($arr))]; // OR
reset(array_values($arr));
The benefit of $arr[reset(array_keys($arr))]; is that it raises an warning if the array is actually empty.
Test if the a variable is an array before getting the first element. When dynamically creating the array if it is set to null you get an error.
For Example:
if(is_array($array))
{
reset($array);
$first = key($array);
}
We can do
$first = reset($array);
Instead of
reset($array);
$first = current($array);
As reset()
returns the first element of the array after reset;
You can make:
$values = array_values($array);
echo $values[0];
Use reset() function to get the first item out of that array without knowing the key for it like this.
$value = array('foo' => 400, 'bar' => 'xyz');
echo reset($value);
output //
400
Starting with PHP 7.3.0 it's possible to do without resetting the internal pointer. You would use array_key_first. If you're sure that your array has values it in then you can just do:
$first = $array[array_key_first($array)];
More likely, you'll want to handle the case where the array is empty:
$first = (empty($array)) ? $default : $array[array_key_first($array)];
You can try this.
To get first value of the array :-
<?php
$large_array = array('foo' => 'bar', 'hello' => 'world');
var_dump(current($large_array));
?>
To get the first key of the array
<?php
$large_array = array('foo' => 'bar', 'hello' => 'world');
$large_array_keys = array_keys($large_array);
var_dump(array_shift($large_array_keys));
?>
In one line:
$array['foo'] = 400;
$array['bar'] = 'xyz';
echo 'First value= ' . $array[array_keys($array)[0]];
Expanded:
$keys = array_keys($array);
$key = $keys[0];
$value = $array[$key];
echo 'First value = ' . $value;
You could use array_values
$firstValue = array_values($array)[0];
You could use array_shift
I do this to get the first and last value. This works with more values too.
$a = array(
'foo' => 400,
'bar' => 'xyz',
);
$first = current($a); //400
$last = end($a); //xyz