How to dynamically set array keys in php

瘦欲@ 提交于 2019-12-04 02:35:58

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.

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.

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!