function values($id,$col)
{
$vals = [1=>['name'=>'Lifting Heavy Boxes']];
return $vals[$id][$col];
}
$complete = [1=>["id"=>"2","sid"=>"35","material_completed"=>"1","date"=>"2017-12-18"]];
$form = 'my_form';
array_walk($complete, function(&$d,$k) use($form) {
$k = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
echo 'in walk '.$k."\n";
});
print_r($complete);
the echo outputs:
in walk Lifting Heavy Boxes [12/18/17] (my_form)
the print_r outputs:
Array
(
[1] => Array
(
[id] => 2
[sid] => 35
[material_completed] => 1
[date] => 2017-12-18
)
)
I have another array walk that is very similar that is doing just fine. The only difference I can perceive between them is in the one that's working, the value $d is already a string before it goes through the walk, whereas in the one that's not working, $d is an array that is converted to a string inside the walk (successfully, but ultimately unsuccessfully).
Something I'm missing?
And here's the fixed version:
array_walk($complete, function(&$d,$k) use($form) {
$d = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
});
That's what I was trying to do anyway. I wasn't trying to change the key. I was under the mistaken impression that to change the value you had to set the key to the new value.
You cannot change the key of the array inside the callback of array_walk():
Only the values of the array may potentially be changed; its structure cannot be altered, i.e., the programmer cannot add, unset or reorder elements. If the callback does not respect this requirement, the behavior of this function is undefined, and unpredictable.
This is also mentioned in the first comment:
It's worth nothing that array_walk can not be used to change keys in the array.
The function may be defined as (&$value, $key) but not (&$value, &$key).
Even though PHP does not complain/warn, it does not modify the key.
Related
I am trying to make a simple class that I can use to check if a value exists within an array. There is a session that contains multiple tool values. I am trying to pass the toolID to this function as well as a key and see if that value exists.
Session Data:
Array
(
[keyring] => Array
(
[tool] => Array
(
[toolID] => 1859
[keys] => Array
(
[0] => 49
[1] => 96
)
)
)
)
class Keyring
{
public function checkKey($key, $toolID){
$keyring = $_SESSION['keyring'];
if(isset($keyring)){
foreach($keyring['tool'] as $k => $v) {
if($k == 'toolID' && $v == $toolID){
if (in_array($key, $k->keys)){
return true;
}
}
}
}
return false;
}
}
$keyring = new Keyring();
print_r($keyring->checkKey(49, 1859));
In this example, I am trying to see if key 49 exists in the session for tool 1859.
I am getting the following error : Warning: in_array() expects parameter 2 to be array, null given in.
Is there a better approach for this? All I am looking for is a true/false as to whether that key exists in the keys array for the specified tool.
For my code to work for you may need to change your array a bit or change the code a bit, but rather then looping through a bunch of keys trying to find the right one we are just looking for the keys in the array if they are not set, then return false, or if we find keyring - tools - $tool_id - key_{$key_id} we are just going to return that value, if key_49 = false the function returns false. This should be a tad bit quicker to run on the server for you.
function has_keyring($tool_id, $key_id)
{
//Just to keep the code tidy let's store the tools key in $tool variable
$tool = $_SESSION['keyring']['tools'];
if(isset($tool[$tool_id]) && isset($tool[$tool_id]["key_{$key_id}"]))
return $tool[$tool_id]["key_{$key_id}"];
return false;
}
Could someone explain me, why this code works properly without crashing initial array structure?
function setArrayValueByPath($path, $value, &$array)
{
foreach ($path as $p) {
$array = &$array[$p];
}
$array = $value;
return true;
}
$array = [
'a' => 'v1',
'b' => 'v2',
];
setArrayValueByPath(['hello', 'world'], '!!!', $array);
echo '<pre>';
print_r($array);
echo '</pre>';
When I run the code, I see:
Array
(
[a] => v1
[b] => v2
[hello] => Array
(
[world] => !!!
)
)
Due to the line in function:
$array = $value;
it should replace $array value, but it does not happen.
My function is based on code snippets are given here: Using a string path to set nested array data
Thank you.
Let's examine this one step at a time.
The parameter $array is a local variable within the function which contains a reference to some external array being passed in.
foreach ($path as $p) {
This iterates over ['hello', 'world']
$array = &$array[$p];
Take the original array, and "index" it with $p (i.e. [hello]). This does not currently exist so it is added to the original array. Then take a reference to that new member and save it in the local variable $array. I.e. you just created a new member of the original array, and the local variable $array no longer points to the original external array.
On the second iteration, take the variable currently pointed to by $array (see just above) and index it with $p (world). This does not exist, so create it.
}
At this point $array points to the member {original array}[hello][world]. I use the syntax {original array} here because you no longer have a reference to it, only a reference to an array two levels nested within it.
$array = $value;
This sets the value of that member to !!!, giving exactly the data structure you see.
Here is the function I wrote to flatten the multidimensional PHP array:
function flattenArray(array $array) {
if (! is_array($array)) {
throw new Exception ("Please specify an array.");
}
$resultArray = [];
$arrayObject = new RecursiveArrayIterator($array);
foreach(new RecursiveIteratorIterator($arrayObject) as $key => $value) {
$resultArray[$key] = $value;
}
return $resultArray;
}
And using it:
$arr = [
["sitepoint", "phpmaster"],
["buildmobile", "rubysource"],
["designfestival", "cloudspring"],
"not an array"
];
print_r(flattenArray($arr));
Result:
Array
(
[0] => designfestival
[1] => cloudspring
[3] => not an array
)
However, I was expecting:
0: sitepoint
1: phpmaster
2: buildmobile
3: rubysource
4: designfestival
5: cloudspring
6: not an array
But it is re-generating indexes as in:
0: sitepoint
1: phpmaster
0: buildmobile
1: rubysource
0: designfestival
1: cloudspring
3: not an array
So how do I modify function to get all elements of the array not just three:
Array
(
[0] => designfestival
[1] => cloudspring
[3] => not an array
)
Thanks for the help
if (!is_array($array)) is superfluous, since you have the array type hint in the function signature and PHP will enforce that.
You are overwriting the keys. Those elements all have the same keys in their respective subarray. Since it's not an associative array, you don't need to preserve the keys. Instead of
$resultArray[$key] = $value;
just do
$resultArray[] = $value;
I too hit this limitation with RecursiveIteratorIterator.
At first I had been using this concise, one-line array flattener wherever needed:
$outputs = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator([$inputs])), FALSE);
similar to your longer function above.
All was great: I was able to "normalize" my data structure into a 1D array, no matter if the incoming $inputs parameter came into my Symfony2 Controller as a single String/float value, 1D or 2+D multidimensional array. (I was writing a callback from AJAX that is to respond with JSON-formatted tables for an interactive Highcharts.com chart to be able to render, in my financial app.)
However, it refused to draw because in the final step, each data cell was in the form
0 => float 100.662
even though I had taken care that my $inputs creature only contained cells in the form:
'2002-04-30' => float 100.662
So basically the above array-flattening line had killed the keys (DateStamp).
Fed up with studying RecursiveIteratorIterator, I just broke down and came up with my own array_flatten that preserves keys, if any:
static public function array_flatten($inObj)
{
$outObj = []; $inObj=[$inObj];
array_walk_recursive($inObj, function ($incell, $inkey) use (&$outObj)
{
$outObj[$inkey] = $incell;
} );
return $outObj;
}
Note that you are responsible for ensuring that the keys in $inObj are globally unique (and either string or int type), otherwise, I don't know how my function behaves. Probably overwrites the value using the same key name?
I have an Eventbus that takes a filter name as its first parameter and a Closure as second parameter. Like this:
$this->EventBus->subscribe('FilterTestEvent', function(){/*Do Something*/});
It's called like this:
$filteredValue = $this->EventBus->filter('FilterTestEvent', $anyValue);
What I want now is to pass an array as reference to the Closure that then is changed in any way (here: add elements) and then return something as the filtered value:
$item_to_change = array('e1' => 'v1', 'e2' => 'v2');
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return true;
});
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
Now I would a print_r($item_to_change) expect to look like the following:
Array
(
[e1] => v1
[e2] => v2
[new] => LoremIpsum
)
But instead it looks like the original array:
Array
(
[e1] => v1
[e2] => v2
)
The eventbus internally stores all closures and calls them if needed through call_user_func_array() with the closure as first argument and the value as the only argument array element.
How can I achieve what it's meant to do?
Source Code to the Eventbus: http://goo.gl/LAAO7B
Probably this line:
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
is supposed to return a new filtered array, not modify the original one.
So check it:
print_r($filtered);
Passing by reference is possible by modifying a function (adding &):
function filter(&$array){ //Note & mark
$array['new_index'] = "Something new" ;
}
$array = array("a"=> "a");
filter($array); //The function now receives the array by reference, not by value.
var_dump($array); //The array should be modified.
Edit:
Make your callback return the filtered array:
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return $item ;
});
Passing by reference should not work here, because in the source code that $value variable is swapped with another value and returned after.
Ok. I found the answer. The filter function needs to be changed so that it accepts arrays as value, in which I can save the reference. For details see difference Revision 1 and Revision 2 of the Eventbus source code, here: goo.gl/GBocgl
I am using Cassandra and I have saved some byte representations as ID. Everything is working fine, however that data (id) is no good for output.
$users = $db->get('1');
echo '<pre>';
print_r($users);
die();
Outputs
Array
(
[��� X��W��c_ ] => Array
(
[id] => ��� X��W��c_
[name] => steve
[surname] => moss
)
[�*B�X��y�~p��~] => Array
(
[id] => �*B�X��y�~p��~
[name] => john
[surname] => doe
)
)
As you can see ID's are some wierd characters, it's because they are byte representations in database. They actually look like \xf5*B\xa0X\x00\x11\xe1\x99y\xbf~p\xbc\xd1~.
In PHPCASSA there is function CassandraUtil::import(); to which I can pass these bytes and it will return guid. It works fine, but I want my array to automatically converted from bytes to guids.
Only option I find is looping through every item in array and assigning new value to it. Somehow I think that it is not the best approach. Is there any other ways to do this?
TL;DR
Have array with bytes like above, need to use CassandraUtil::import(); on array keys and id's to get readable id's. What is the most effective way of doing so.
UPDATE
Sorry, only saw the top level array key, I think you would have to run the function below as well as another one after:
function cassImportWalkRecur(&$item, $key)
{
if ($key == 'id')
$item = CassandraUtil::import();
}
$array = array_walk_recursive($array, 'cassImportWalkRecur');
That should apply it to the ID fields. If you need to check the data first, there maybe a way to detect the encoding, but I am not sure how to do that.
You should be able to create a function and use array_walk to traverse the array and update the keys. Something like:
function cassImportWalk($item, &$key)
{
$key = CassandraUtil::import();
}
$array = array_walk($array, 'cassImportWalk');
Untested (also you may have to change the CassandraUtil usage), but should work.
Unless I am misunderstanding the question this can be done simply and cleanly like so:
$users = $db->get('1');
$keys = array_keys($users);
$readableKeys = array_map("CassandraUtil::import",$keys);
foreach($users as $currentKey => $subArray) {
$readableKey = array_shift($readableKeys);
$subArray['id'] = $readableKey;
$users[$readableKey] = $subArray;
unset($users[$currentKey]);
}
Would array_flip() all keys and values, then array_walk() and apply my function, before doing a final array_flip().