I've just got an unexpected result for a simple function which has an argument passed by reference.
Let's presume we have the following arrays:
$arr = array(
'Test' => 1,
'OtherKey' => 2,
);
$keyTranslation = array(
'OtherKey' => 'other_key',
'Test' => 'test',
);
The $keyTranslation array might not have the keys defined in the same order as the $arr. This is just to explain the need of the function.
and the following function:
function test(&$arr, $keyTranslation) {
foreach ($arr as $key => $value) {
$arr[$keyTranslation[$key]] = $value;
unset($arr[$key]);
}
}
The unexpected result is when simply call the function having the arrays above as arguments:
test($arr, $keyTranslation);
What I was expected?
array(2) {
["test"]=>
int(1)
["other_key"]=>
int(2)
}
What I've got?
NOTICE Undefined index: test on line number 10
NOTICE Undefined index: other_key on line number 10
NOTICE Undefined index: on line number 10
array(0) { }
Why was this happened?
Because each time I am adding a new value to the passed by reference array, the loop is iterating also over that value and unset it.
The question
Is this normal? or it is a bug in PHP?
This is what I would use as it seems a bit dodgy to be setting & unsetting elements of an array while foreaching through it...
function test(&$arr, $keyTranslation) {
$newarr = array();
foreach ($arr as $key => $value) {
$newarr[$keyTranslation[$key]] = $value;
}
$arr = $newarr; // Not sure if you'd have to unset $arr first...
}
Make sure you test if the translated key exists :
<?php
$arr = [ 'Foo' => 1, 'Bar' => 2 , 'dont_change' => 3, ];
$trans = [ 'Foo' => 'bar', 'Bar' => 'foo', 'Foobar' => 'foobar', ];
function test(&$arr, $trans) {
foreach($arr as $key => $value) {
if (!isset($trans[$key])) continue;
$arr[$trans[$key]] = $value;
unset($arr[$key]);
}
}
test($arr, $trans);
print_r($arr);
No Bug here. First of all the keys in $arr are 'Test' and 'OtherKey'. In your function you try to access $arr['test'] and $arr['other_key'], which do not exist, hence the notices. Then you unset the key and therefore the result is that $arr is null after the function call, as you passed $arr by reference.
Related
Here my array:
$array = [
'key1' => [
'first' => 'azerty',
'second' => 'qwerty'
],
'key2' => [
'first' => 'hello',
'second' => 'world'
]
];
With the value 'qwerty' I would like to retrieve the 'key1'.
I looking for something like that:
$theKeyIWant = array_search('qwerty', array_column($array, 'second'));
But I get '0', instead of 'key1' (and I know that's how it works)
Anyone know how to adapt this code or know another code to get the key value?
Slight modification to your code to combine the keys and column values:
$theKeyIWant = array_search('qwerty', array_combine(array_keys($array), array_column($array, 'second')));
The problem with seeking "something sexier" for this task is that if "sexier" means a "functional iterator", then that comes a cost of not being able to "return early" (doing unnecessary cycles).
If you want a single-line function to call, you can build your own and put it in a helpers file somewhere in your project. My strong advice is to abandon "sexy" for this task and use a breakable foreach loop.
Code: (Demo)
function getRowKey($array, $column, $value) {
foreach ($array as $key => $row) {
if ($row[$column] === $value) {
return $key;
}
}
return null;
}
var_export(getRowKey($array, 'second', 'qwerty'));
If you are going to perform repeated searches on the same array and the second column is guaranteed to contain unique values, then you can convert the array into a lookup array without losing any data. (Demo)
function restructure($array, $columnToKey) {
$result = [];
foreach ($array as $key => $row) {
$result[$row[$columnToKey]] = $row + ['oldKey' => $key];
}
return $result;
}
var_export(restructure($array, 'second')['qwerty']);
To solve this is to loop through the outer array and use array_search() to search for the value within each inner array.
$value = 'qwerty';
$theKeyIWant = null;
foreach ($array as $key => $innerArray) {
if (array_search($value, $innerArray) !== false) {
$theKeyIWant = $key;
break;
}
}
echo $theKeyIWant; // Output: key1
array_keys returns an array of an array's keys.
<?php
$array = [
'key1' => [
'first' => 'azerty',
'second' => 'qwerty'
],
'key2' => [
'first' => 'hello',
'second' => 'world'
]
];
$theKeyIWant = array_search('qwerty', array_column($array, 'second'));
echo array_keys($array)[$theKeyIWant];
?>
3V4l
Given a PHP array that looks like:
[
'foo' => 1,
'bar[0]' => 6,
'bar[1]' => 7,
'bar[2]' => 8,
'baz' => 'anything',
... and so on
]
I want to convert the "implied" nesting into real arrays, while leaving the rest untouched, to result in:
[
'foo' => 1,
'bar' => [6, 7, 8],
'baz' => 'anything',
]
I've searched the php docs but can't find a utility for this. I'm sure I could write a function to do this, but it feels like reinventing the wheel. Surely such a function already exists?
You can use array_walk() and preg_match to see if the key should be an "array". We can then pass in our final array by reference to allow us to edit it.
For example
<?php
$a = [
'foo' => 1,
'bar[0]' => 6,
'bar[1]' => 7,
'bar[2]' => 8,
'baz' => 'anything',
];
$end = [];
array_walk($a, function($val, $key) use(&$end) {
//See if the key is something like "bar[1]"
if( preg_match("/^([a-z]+)\[[0-9]+\]$/", $key, $match) ) {
//See if "bar" key exists in our final array, if not create it.
if( array_key_exists($match[1], $end) == FALSE ) {
return $end[$match[1]] = array($val);
}
//Add value to array we created above
return $end[$match[1]][] = $val;
}
//It's just a normal key, so just add it to our final array
return $end[$key] = $val;
});
print_r($end);
https://eval.in/315998
just playing around. See comments for explanation of code.
/*
our source array
*/
$a = array(
'foo' => 1,
'bar[0]' => 6,
'bar[1]' => 7,
'bar[2]' => 8,
'baz' => 'anything'
);
// an array declared to hold all variable names present in array.
$vars = array();
/*
http://php.net/manual/en/function.extract.php
extract all values from the array with keys are variable names. Keys like
bar[0] do not make sense to extract function so it ignores them.
*/
extract($a);
/*
Now that we've got all variables we possibly could using extract(), we
traverse the source array to create the $bar array ourselves.
*/
foreach($a as $k => $v) {
/*
if key contains a [
this check could be rigorous, but I leave that to the production code developer
*/
if(strstr($k, '[')) {
/*
replace the [number] part from key to get the array name, i.e., "bar"
*/
$arr_name = preg_replace('/\[\d+\]/', '', $k);
/*
using variable variables feature (http://php.net/manual/en/language.variables.variable.php)
check if we've created the array already. if not, create now. and
record the variable name in $vars array for future reference
*/
if(!is_array($$arr_name)) {
$$arr_name = array();
$vars[] = $arr_name;
}
/*
formulate and eval() (http://php.net/manual/en/function.eval.php)
a statement that inserts current $v into our created array
eval is evil so do some rigorous testing before using it
*/
eval('$' . $k . '=' . $v . ';');
}
else{
//otherwise just record the variable.
$vars[] = $k;
}
}
/* $vars holds names of all variables you got from stream */
var_dump($vars);
/* display the variables */
var_dump($foo, $bar, $baz);
/* almost forgot, http://php.net/manual/en/function.var-dump.php */
I'm trying to remove an object from an array of objects by its' index. Here's what I've got so far, but i'm stumped.
$index = 2;
$objectarray = array(
0=>array('label'=>'foo', 'value'=>'n23'),
1=>array('label'=>'bar', 'value'=>'2n13'),
2=>array('label'=>'foobar', 'value'=>'n2314'),
3=>array('label'=>'barfoo', 'value'=>'03n23')
);
//I've tried the following but it removes the entire array.
foreach ($objectarray as $key => $object) {
if ($key == $index) {
array_splice($object, $key, 1);
//unset($object[$key]); also removes entire array.
}
}
Any help would be appreciated.
Updated Solution
array_splice($objectarray, $index, 1); //array_splice accepts 3 parameters
//(array, start, length) removes the given array and then normalizes the index
//OR
unset($objectarray[$index]); //removes the array at given index
$reindex = array_values($objectarray); //normalize index
$objectarray = $reindex; //update variable
array_splice($objectarray, $index, 1);
//array_splice accepts 3 parameters (array, start, length) and removes the given
//array and then normalizes the index
//OR
unset($objectarray[$index]); //removes the array at given index
$reindex = array_values($objectarray); //normalize index
$objectarray = $reindex; //update variable
You have to use the function unset on your array.
So its like that:
<?php
$index = 2;
$objectarray = array(
0 => array('label' => 'foo', 'value' => 'n23'),
1 => array('label' => 'bar', 'value' => '2n13'),
2 => array('label' => 'foobar', 'value' => 'n2314'),
3 => array('label' => 'barfoo', 'value' => '03n23')
);
var_dump($objectarray);
foreach ($objectarray as $key => $object) {
if ($key == $index) {
unset($objectarray[$index]);
}
}
var_dump($objectarray);
?>
Remember, your array will have odd indexes after that and you must (if you want) reindex it.
$foo2 = array_values($objectarray);
in that case you won't need that foreach just unset directly
unset($objectarray[$index]);
I've got this error Warning: strtolower() expects parameter 1 to be string, array given..
I don't know how I've got my error.. Please help thanks!
$mypages = array(
'Pages' => array('page' => array('view_all_pages', 'add_page', 'dashboard'),
'test' => array('test1', 'test2')),
'Users' => array('vieW_all_users', 'add_user'));
foreach($mypages as $keys => $key):
if(is_array($key)):
$key = array_map('strtolower' ,$key);
endif;
endforeach;
foreach is a loop that will gives you first level of key=>value pairs of an array.
foreach($mypages as $keys => $key){
echo "Key : $keys \n";
echo "Value : ";var_dump($key);
}
will output :
Key : Pages
Value : array('page' => array('view_all_pages', 'add_page', 'dashboard'), 'test'=> array('test1', 'test2')),
Key : Users
Value : array('vieW_all_users', 'add_user')
To make it work, you need to check if the value is an array.
function strtolowerArray(&$arr){
foreach($arr as $k=>$v){
if(is_array($v)){
$arr[$k] = strtolowerArray($v);
}
else if(is_string($v)){
$arr[$k] = strtolower($v);
}
else{
throw new \LogicException("The value is neither a string nor an array");
}
}
return $arr;
}
$mypages = array(
'Pages' => array(
'page' => array('view_ALL_pages', 'aDD_page', 'DaShbOArd'),
'test' => array('test1', 'TEST2')
),
'Users' => array('vieW_all_users', 'aDd_uSer')
);
var_dump(strtolowerArray($mypages));
The '&' in front of the strtolowerArray's parameter means we pass teh variable by reference. If anychanges happens to this variables inside strtolowerArray function's scope, then it will be reflected into the parent scope.
$mypages['Pages']['page'] does not contain a string that can be passed to strtolower().
You should debug by dumping $key inside the loop.
You have arrays in your arrays yo!
You have to iterate into the arrays before asking for (and trying to convert) the values.
Try walking through the array conditionally e.g.
//pseudo
func myRecursion($data = array()) {
foreach($data as $value) {
if(is_array($value)) {
$data = myRecusion($data[$value]);
} else {
//its not an array so do your thing
}
}
return($data);
}
I am sure that this is super easy and built-in function in PHP, but I have yet not seen it.
Here's what I am doing for the moment:
foreach($array as $key => $value) {
echo $key; // Would output "subkey" in the example array
print_r($value);
}
Could I do something like the following instead and thereby save myself from writing "$key => $value" in every foreach loop? (psuedocode)
foreach($array as $subarray) {
echo arrayKey($subarray); // Will output the same as "echo $key" in the former example ("subkey"
print_r($value);
}
Thanks!
The array:
Array
(
[subKey] => Array
(
[value] => myvalue
)
)
You can use key():
<?php
$array = array(
"one" => 1,
"two" => 2,
"three" => 3,
"four" => 4
);
while($element = current($array)) {
echo key($array)."\n";
next($array);
}
?>
Use the array_search function.
Example from php.net
$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');
$key = array_search('green', $array); // $key = 2;
$key = array_search('red', $array); // $key = 1;
$foo = array('a' => 'apple', 'b' => 'ball', 'c' => 'coke');
foreach($foo as $key => $item) {
echo $item.' is begin with ('.$key.')';
}
$array = array(0 => 100, "color" => "red");
print_r(array_keys($array));
If it IS a foreach loop as you have described in the question, using $key => $value is fast and efficient.
If you want to be in a foreach loop, then foreach($array as $key => $value) is definitely the recommended approach. Take advantage of simple syntax when a language offers it.
Another way to use key($array) in a foreach loop is by using next($array) at the end of the loop, just make sure each iteration calls the next() function (in case you have complex branching inside the loop)
Try this
foreach(array_keys($array) as $nmkey)
{
echo $nmkey;
}
Here is a generic solution that you can add to your Array library. All you need to do is supply the associated value and the target array!
PHP Manual: array_search() (similiar to .indexOf() in other languages)
public function getKey(string $value, array $target)
{
$key = array_search($value, $target);
if ($key === null) {
throw new InvalidArgumentException("Invalid arguments provided. Check inputs. Must be a (1) a string and (2) an array.");
}
if ($key === false) {
throw new DomainException("The search value does not exists in the target array.");
}
return $key;
}