How to dynamically set array keys in php

How to dynamically set array keys in php



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?






Might this be better managed using objects?

– Jake N
Sep 13 '10 at 20:43






I don't actually understand the problem. Are you trying to query these data structures in a uniform way?

– erisco
Sep 13 '10 at 20:52






I made an edit to help explain. Hopefully it does help.

– Kramer
Sep 13 '10 at 21:28






can you give some sample data? Are you littarlly trying to determine whether the name of the key is UnitType or Resource, or do UnitType and Resource actually store dynamic content in the keys?

– Eric Leroy
Oct 14 '12 at 11:47






@Kramer, add placeholders for user entered values. Make your arrays uniform.

– Mohal
Jan 1 '18 at 8:27




6 Answers
6



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.






I'm not sure that the question was clear. I have made some edits to try to help explain.

– Kramer
Sep 13 '10 at 21:28



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:


MeterReading


MeterReading


$calcs


// 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.


resource



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'].


getVal($data,array('foo','bar','2017-08'))


$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.


setVal($data,array('foo','bar','2017-08'),'hello')


$data['foo']['bar']['2017-08'] = 'hello'



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.


dict_set()


$path



It can also append incremental members to deeply nested array by using as an element of the $path. For instance: parent.child.child.



$path


parent.child.child.



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.


$data



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)