I have the following array:
$people['men'] = [
'first_name' => 'John',
'last_name' => 'Doe'
];
And I have the following flat array:
$name = ['men', 'first_name'];
Now I want to create a function that "reads" the flat array and gets the value from the multidimensional array, based on the sequence of the elements of the flat array.
function read($multidimensionalArray,$flatArray){
// do stuff here
}
echo read($people,$name); // must print 'John'
Is this even possible to achieve? And which way is the way to go with it? I'm really breaking my head over this. I have no clue at all how to start.
Thanks in advance.
This should to the trick:
<?php
$people['men'] = [
'first_name' => 'John',
'last_name' => 'Doe'
];
$name = ['men', 'first_name'];
echo read($people,$name);
function read($multidimensionalArray,$flatArray){
$cur = $multidimensionalArray;
foreach($flatArray as $key)
{
$cur = $cur[$key];
}
return $cur;
}
Link: https://3v4l.org/96EnQ
Be sure to put some error checking in there (isset and the likes)
You can use a recursive function to do this.
function read(&$array, $path) {
// return null if one of the keys in the path is not present
if (!isset($array[$key = array_shift($path)])) return null;
// call recursively until you reach the end of the path, then return the value
return $path ? read($array[$key], $path) : $array[$key];
}
echo read($people, $name);
You could also use array_reduce
$val = array_reduce($name, function($carry, $item) {
return $carry[$item];
}, $people);
looks like you just want:
echo $multidimensionalArray[$flatArray[0]][$flatArray[1]];
Related
Is there a way to access a PHP multidimensional array (specific index) dynamically?
So for example, say I want to access a value at:
$array[1]['children'][0]['children'][2]['settings']['price']
Can there be a function which returns this value by just getting index (1, 0 and 2 respectively)? and in a way that it works whether there are 3 values (1, 0 and 2) or more.
SO for example , we call function "getArrayValue()" and we can it like so:
getArrayValue('1,0,2')
this should return
$array[1]['children'][0]['children'][2]['country']['city']
or in case of 2 values
getArrayValue('1,0')
should return
$array[1]['children'][0]['country']['city']
so basically, the issue I am facing is needing help with dynamically building the query to get array value...
Or if there is a way to convert a string like $array[1]['children'][0]['country']['city'] and evaluate it to get this value from the array itself?
Another way to explain my problem:
$arrayIndex = "[1]['children'][0]";
$requiredValue = $array[1] . $arrayIndex . ['country']['city'];
//// so $requiredValue should output the same value as the below line would:
$array[1]['children'][0]['children'][2]['country']['city'];
Is there a way to achieve this?
In your case you will need something like this:
<?php
function getArray($i, $j, $k, $array) {
if (isset($array[$i]['children'][$j]['children'][$k]['country']['city']))
{
return $array[$i]['children'][$j]['children'][$k]['country']['city'];
}
}
$array[0]['children'][0]['children'][0]['country']['city'] = "some value";
$array[0]['children'][0]['children'][1]['country']['city'] = "another";
$array[0]['children'][1]['children'][1]['country']['city'] = "another one";
.
.
.
etc
echo getArray(0,0,0, $array) . "\n"; // output -> "some value"
echo getArray(0,0,1, $array) . "\n"; // output -> "another"
echo getArray(0,1,1, $array) . "\n"; // output -> "another one"
Another thing to keep in mind is that you have called the function passing only one parameter. And your multidimensional array needs at least three.
getArrayValue('1,0,2')
You have to take into account that you have called the function passing only one parameter. Even if there were commas. But it's actually a string.
getArrayValue(1,0,2) //not getArrayValue('1,0,2') == string 1,0,2
If you want to pass two values, you would have to put at least one if to control what you want the function to execute in that case. Something like:
function getArray($i, $j, $k, $array) {
if($k==null){
if(isset($array[$i]['children'][$j]['country']['city'])){
return $array[$i]['children'][$j]['country']['city']; //
}
} else {
if(isset($array[%i]['children'][$j]['children'][$k]['country']['city'])){
return $array[$i]['children'][$j]['children'][$k]['country']['city'];
}
}
}
getArray(0,0,null, $array)
getArray(0,0,1, $array)
For the last question you can get by using the eval() function. But I think it's not a very good idea. At least not recommended. Example:
echo ' someString ' . eval( 'echo $var = 15;' );
You can see the documentation: https://www.php.net/manual/es/function.eval.php
edit:
I forgot to mention that you can also use default arguments. Like here.
<?php
$array[0]['children'][0]['children'][0]['country']['city'] = "some value";
$array[0]['children'][0]['children'][1]['country']['city'] = "another";
$array[0]['children'][1]['children'][1]['country']['city'] = "another one";
function getArray($array,$i, $j, $k = null) {
if($k==null){
echo 'getArray called without $k argument';
echo "\n";
}
else{
echo 'getArray called with $k argument';
echo "\n";
}
}
getArray($array,0,0); //here you call the function with only 3 arguments, instead of 4
getArray($array,0,0,1);
In that case $k is optional. If you omit it, the default value will be null. Also you have to take into account that A function may define default values for arguments using syntax similar to assigning a variable. The default is used only when the parameter is not specified; in particular, note that passing null does not assign the default value.
<?php
function makecoffee($type = "cappuccino"){
return "Making a cup of $type.\n";
}
echo makecoffee();
echo makecoffee(null);
echo makecoffee("espresso");
The above example will output:
Making a cup of cappuccino.
Making a cup of .
Making a cup of espresso.
You can read more about that here: https://www.php.net/manual/en/functions.arguments.php
I find #Dac2020's answer to be too inflexible to be helpful to future researchers. In fact, the overly specific/niche requirements in the question does not lend this page to being very helpful for future researchers because the processing logic is tightly coupled to the array structure.
That said, I've tried to craft a function that builds best practices of checking for the existence of keys and D.R.Y. principles in the hope that future researchers will be able to easily modify it for their needs.
Inside the custom function, the first step is to split the csv into an array then interweave the static keys between the dynamic keys as dictated by the asker. In more general use cases, ALL keys would be passed into the function thus eliminating the need to prepare the array of keys.
As correctly mentioned by #MarkusAO in a comment under the question, this question is nearly a duplicate of Convert dot syntax like "this.that.other" to multi-dimensional array in PHP.
Code: (Demo)
function getValue($haystack, $indexes) {
$indices = explode(',', $indexes);
$finalIndex = array_pop($indices);
$keys = [];
foreach ($indices as $keys[]) {
$keys[] = 'children';
}
array_push($keys, $finalIndex, 'country', 'city');
//var_export($keys);
foreach ($keys as $level => $key) {
if (!key_exists($key, $haystack)) {
throw new Exception(
sprintf(
"Path attempt failed for [%s]. No `%s` key found on levelIndex %d",
implode('][', $keys),
$key,
$level
)
);
}
$haystack = $haystack[$key];
}
return $haystack;
}
$test = [
[
'children' => [
[
'children' => [
[
'country' => [
'city' => 'Paris',
]
],
[
'country' => [
'city' => 'Kyiv',
]
]
]
]
]
],
[
'children' => [
[
'country' => [
'city' => 'New York',
]
],
[
'country' => [
'city' => 'Sydney',
]
]
]
]
];
$result = [];
try {
$result['0,0,0'] = getValue($test, '0,0,0');
$result['1,0'] = getValue($test, '1,0');
$result['1,0,0'] = getValue($test, '1,0,0');
} catch (Exception $e) {
echo $e->getMessage() . "\n---\n";
}
var_export($result);
Output:
Path attempt failed for [1][children][0][children][0][country][city]. No `children` key found on levelIndex 3
---
array (
'0,0,0' => 'Paris',
'1,0' => 'New York',
)
I have a string 'value1/value2'. The required output is $_SESSION['value1']['value2']. i tried using explode and then array_reduce over explode values but with no success.
My code looks like
function set($key, $value){
/* code */
}
set('key1/key2', 'some_text');
required output like $_SESSION['key1']['key2'] = 'some_text';
key1/key2 is not fixed it may be 'key1' or 'key1/key2/key3' and so on.
Anyone be make fiddle of it is Highly appreciable.
Thanks
Accessing a value via a key-path string, as in your original question, using your original idea and let array_reduce do the work looks like:
$session = ['value1' => [ 'value2' => [ 'value3' => 'there you are!' ]]];
$path = explode('/', 'value1/value2/value3');
$val = array_reduce($path,
function(&$carry, $key) { return $carry[$key];},
$session);
echo $val
--> "there you are!"
Setting a value can be done e.g. like this, following the path by reference, creating arrays as needed:
function set($path, $value) {
$path = explode('/', $path);
$key = array_pop($path);
$arr = &$_SESSION;
foreach($path as $part) {
// carefull, this might lose values to accommodate
// the structure wanted with $path
(isset($arr[$part]) && is_array($arr[$part])) || ($arr[$part] = []);
$arr =& $arr[$part];
}
$arr[$key] = $value;
};
Try this
<?php
session_start();
$string = 'value1/value2';
$array = explode("/",$string);
$_SESSION[$array[0]][$array[1]] = "ccccccc";//$_SESSION['value1']['value2']
For a general case (i.e. for more than two pieces), you'll need to iterate over the segments, and incrementally index further into your target array:
<?php
$string = 'value1/value2/value3';
$_SESSION = ['value1' => ['value2' => ['value3' => 'My String']]];
$target = $_SESSION;
foreach (explode('/', $string) as $piece) {
$target =& $target[$piece];
}
echo $target; // My String
I want to pass one argument to a function, rather than multiple arguments, that tend to grow unexpectedly. So I figure an array will get the job done. Here's what I've drafted so far...
<?php
function fun_stuff($var){
// I want to parse the array in the function, and use
}
$my = array();
$my['recordID'] = 5;
$my['name'] = 'John Smith';
$my['email'] = 'john#someemail.com';
echo fun_stuff($my);
?>
I haven't quite grasped the concept of parsing an array. And this is a good way for me to learn. I generally pass the same variables, but on occasion a record does not have an email address, so I do need to make a condition for missing keys.
Am I doing this right so far? Can I pass an array as an argument to a function?
And if so, how do I parse and search for existing keys?
Hopefully this isn't too far off topic...but you sounded like you were just trying to avoid multiple parameters when some can be NULL. So, I would recommend that you use an object instead of an array for clarity...that way, there is no confusion as to what properties should exist. If you're using PHP 5, you can also strongly type the parameter so nothing else can get in. So:
class Record {
public $Id;
public $Name;
public $Email
}
function fun_stuff( Record $record ) {
// you will now have better intellisense if you use an IDE
// and other develoers will be able to see intended parameters
// clearly, while an array would require them to know what's
// intended to be there.
if( !empty($record->Email) ) {
// do whatever.
}
}
Yes you are on the right track. The approach I take is put required paramters as the first parameters and all optional parameters in the last argument which is an array.
For example:
function fun_stuff($required1, $required2, $var = array()) {
// parse optional arguments
$recordId = (key_exists('recordID', $var) ? $var['recordId'] : 'default value');
$name = (key_exists('name', $var) ? $var['name'] : 'default value');
$email = (key_exists('email', $var) ? $var['email'] : 'default value');
}
Then you can call your function like so:
fun_stuff('val 1', 'val 2', array(
'recordId' => 1,
'name' => 'John',
'email' => 'john#stackoverflow.com'
));
This is a bad design practice, but that's not the question here. You can "parse" array's like so...
if( array_key_exists( 'email', $var ))
{
// use email field
}
If you need to, you can loop through all elements like so...
foreach( $var as $key => $value )
{
echo '$var[\''.$key.'\'] = '.$value;
}
I'm not recommend you to use array for this.
You can define optional arguments with default values:
//$name and $email are optional here
function fun($record_id, $name='', $email='')
{
if (empty($name)) print '$name is empty';
}
//Usage:
fun(5, 'Robert');
fun(5);
fun(5, 'Robert', 'robert#gmail');
fun(3,'','robert#gmail');
If you will use array, IDE will not be able to show autocomplete suggestions, it means more typos, and you have to remember all keys of this array forever or look at code of the function each time.
I'm not really sure what you want to achieve, but I suspect something like this:
$aPersons = array();
$aPersons[] = array('name' => 'name1', 'age' => 1);
$aPersons[] = array('name' => 'name2', 'age' => 2);
array_map('parsePerson', $aPersons);
function parsePerson($aPerson) {
echo $aPerson['name'];
echo $aPerson['age'];
}
The problem with your current array is that it only has one dimension.
You can simple do echo $my['name'];. There are easier ways to parse arrays though.
foreach($aPersons as $aPerson) {
echo $aPerson['name'];
echo $aPerson['age'];
}
$iLength = sizeof($aPersons);
for($i = 0; $i <= $iLength; $i++) {
echo $aPersons[$i]['name'];
echo $aPersons[$i]['age'];
}
To parse and view, there is the signficant print_r function which gives out the array details.
When calling a function you need the return syntax at the end that will parse out anything you call in the return.
You obviously can pass array to the function. Inside it read the variable as you were assigning values before it. If you assign:
$my['key'] = 'value';
In you function use:
echo $var['key'];
Why you don't use a foreach to walk in array?
function fun_stuff($var){
foreach($var as $key => $item){
echo '[', $key, "] => ", $item, "\n";
}
}
$my = array();
$my['recordID'] = 5;
$my['name'] = 'John Smith';
$my['email'] = 'john#someemail.com';
fun_stuff($my);
Yes, this is correct (though your question is a bit broad). You're already referencing the array values via indexes when you set up the $my variable. You can do the same thing within your function (with the $var variable).
I recommend taking a look at all of PHP's built-in array functions: http://php.net/manual/en/ref.array.php
Try this:
function fun_stuff($var){
// I want to parse the array in the function, and use
$fun_string = "";
if( is_array( $var ) {
if( array_key_exists( "name", $var ) )
$fun_string .= "For " . $var["name"];
else $fun_string .= "A nameless one ";
if( array_key_exists( "email", $var ) )
$fun_string .= " (email: " . $var["email"] . ")";
else $fun_string .= " without a known e-mail address";
if( array_key_exists( "recordID", $var ) )
$fun_string .= " has record ID of " . $var["recordID"];
else $fun_string .= " has no record ID set";
$fun_string .= "\n";
}
return $fun_string;
}
Yes You can! Just pass the array and inside the function just use a foreach loop to parse it!
function myFunction($array)
{
foreach($array as $value)
{
echo $value;
}
}
or If you want to have full control over the pair key/value:
function myFunction($array)
{
foreach($array as $key=>$value)
{
echo "key:".$array[$key]."value:".$values;
}
}
I have some logic that is being used to sort data but depending on the user input the data is grouped differently. Right now I have five different functions that contain the same logic but different groupings. Is there a way to combine these functions and dynamically set a value that will group properly. Within the function these assignments are happening
For example, sometimes I store the calculations simply by:
$calcs[$meter['UnitType']['name']] = ...
but other times need a more specific grouping:
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] =...
As you can see sometimes it is stored in a multidiminesional array and other times not. I have been trying to use eval() but without success (not sure that is the correct approach). Storing the data in a temporary variable does not really save much because there are many nested loops and if statements so the array would have to be repeated in multiple places.
EDIT
I hope the following example explains my problem better. It is obviously a dumbed down version:
if(){
$calcs[$meter['UnitType']['name']] = $data;
} else {
while () {
$calcs[$meter['UnitType']['name']] = $data;
}
}
Now the same logic can be used but for storing it in different keys:
if(){
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
} else {
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
}
Is there a way to abstract out the keys in the $calc[] array so that I can have one function instead of having multiple functions with different array keys?
You can use this if you want to get&set array values dynamically.
function getVal($data,$chain){
$level = $data;
for($i=0;$i<count($chain);$i++){
if(isset($level[$chain[$i]]))
$level = $level[$chain[$i]];
else
return null; // key does not exist, return null
}
return $level;
}
function setVal(&$data,$chain,$value){
$level = &$data;
for($i=0;$i<count($chain);$i++){
$level = &$level[$chain[$i]]; // set reference (&) in order to change the value of the object
}
$level = $value;
}
How it works:
Calling getVal($data,array('foo','bar','2017-08')) will return the equivalent of $data['foo']['bar']['2017-08'].
Calling setVal($data,array('foo','bar','2017-08'),'hello') will set value as if you called
$data['foo']['bar']['2017-08'] = 'hello'. non-existent keys will be created automatically by php magic.
This can be useful if you want to build the structure of the array dynamically.
Here's a function I wrote for setting deeply nested members on arrays or objects:
function dict_set($var, $path, $val) {
if(empty($var))
$var = is_array($var) ? array() : new stdClass();
$parts = explode('.', $path);
$ptr =& $var;
if(is_array($parts))
foreach($parts as $part) {
if('[]' == $part) {
if(is_array($ptr))
$ptr =& $ptr[];
} elseif(is_array($ptr)) {
if(!isset($ptr[$part]))
$ptr[$part] = array();
$ptr =& $ptr[$part];
} elseif(is_object($ptr)) {
if(!isset($ptr->$part))
$ptr->$part = array();
$ptr =& $ptr->$part;
}
}
$ptr = $val;
return $var;
}
Using your example data:
$array = [];
$array = dict_set($array, 'resource1.unit1.2017-10', 'value1');
$array = dict_set($array, 'resource1.unit2.2017-11', 'value2');
$array = dict_set($array, 'resource2.unit1.2017-10', 'value3');
print_r($array);
Results in output like:
Array
(
[resource1] => Array
(
[unit1] => Array
(
[2017-10] => value1
)
[unit2] => Array
(
[2017-11] => value2
)
)
[resource2] => Array
(
[unit1] => Array
(
[2017-10] => value3
)
)
)
The second argument to dict_set() is a $path string in dot-notation. You can build this using dynamic keys with period delimiters between the parts. The function works with arrays and objects.
It can also append incremental members to deeply nested array by using [] as an element of the $path. For instance: parent.child.child.[]
Would it not be easier to do the following
$calcs = array(
$meter['Resource']['name'] => array(
$meter['UnitType']['name'] => 'Some Value',
$meter['UnitType']['name2'] => 'Some Value Again'
),
);
or you can use Objects
$calcs = new stdClass();
$calcs->{$meter['UnitType']['name']} = 'Some Value';
but I would advice you build your structure in arrays and then do!
$calcs = (object)$calcs_array;
or you can loop your first array into a new array!
$new = array();
$d = date('Y-m',$start);
foreach($meter as $key => $value)
{
$new[$key]['name'][$d] = array();
}
Give it ago and see how the array structure comes out.
Try to use a switch case.
<?php
$userinput = $calcs[$meter['UnitType']['name']] = $data;;
switch ($userinput) {
case "useriput1":
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
break;
case "userinput2":
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
break;
...
default:
while () {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
}
}
?>
I agree with the comment on the OP by #Jake N that perhaps using objects is a better approach. Nonetheless, if you want to use arrays, you can check for the existence of keys in a conditional, like so:
if(
array_key_exists('Resource', $meter)
) {
$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
} else {
$calcs[$meter['UnitType']['name']] = $data;
}
On the other hand, if you want to use objects, you can create a MeterReading object type, and then add MeterReading instances as array elements to your $calcs array, like so:
// Object defintion
class MeterReading {
private $data;
private $resource;
private $startDate;
private $unitType;
public function __construct(Array $meter, $start, $data) {
$this->unitType = $meter['UnitType']['name'];
$this->resource = $meter['Resource']['name'];
$this->startDate = date('Y-m',$start);
}
public function data() {
return $this->data;
}
public function resource() {
return $this->resource;
}
public function startDate() {
return $this->startDate;
}
public function unitType() {
return $this->unitType;
}
}
// Example population
$calcs[] = new MeterReading($meter, $start, $data);
// Example usage
foreach($calcs as $calc) {
if($calc->resource()) {
echo 'Resource: ' . $calc->resource() . '<br>';
}
echo 'Unit Type: ' . $calc->unitType() . '<br>';
echo 'Start Date: ' . $calc->startDate() . '<br>';
echo 'Data: ' . $calc->data() . '<br>';
}
Obviously you can take this further, such as checking the existence of array keys in the object constructor, giving the object property resource a default value if not provided, and so on, but this is a start to an OO approach.
You can use this library to get or set value in multidimensional array using array of keys:
Arr::getNestedElement($calcs, [
$meter['Resource']['name'],
$meter['UnitType']['name'],
date('Y-m', $start)
]);
to get value or:
Arr::handleNestedElement($calcs, [
$meter['Resource']['name'],
$meter['UnitType']['name'],
date('Y-m', $start)
], $data);
to set $data as value.
Example:
$arr = array(1 => 'Foo', 5 => 'Bar', 6 => 'Foobar');
/*... do some function so $arr now equals:
array(0 => 'Foo', 1 => 'Bar', 2 => 'Foobar');
*/
Use array_values($arr). That will return a regular array of all the values (indexed numerically).
PHP docs for array_values
array_values($arr);
To add to the other answers, array_values() will not preserve string keys. If your array has a mix of string keys and numeric keys (which is probably an indication of bad design, but may happen nonetheless), you can use a function like:
function reset_numeric_keys($array = array(), $recurse = false) {
$returnArray = array();
foreach($array as $key => $value) {
if($recurse && is_array($value)) {
$value = reset_numeric_keys($value, true);
}
if(gettype($key) == 'integer') {
$returnArray[] = $value;
} else {
$returnArray[$key] = $value;
}
}
return $returnArray;
}
Not that I know of, you might have already checked functions here
but I can imagine writing a simple function myself
resetarray($oldarray)
{
for(int $i=0;$i<$oldarray.count;$i++)
$newarray.push(i,$oldarray[i])
return $newarray;
}
I am little edgy on syntax but I guess u got the idea.