Как динамически установить ключи массива в php - PullRequest
9 голосов
/ 14 сентября 2010

У меня есть некоторая логика, которая используется для сортировки данных, но в зависимости от ввода пользователя данные группируются по-разному. Прямо сейчас у меня есть пять разных функций, которые содержат одну и ту же логику, но разные группировки. Есть ли способ объединить эти функции и динамически установить значение, которое будет правильно группироваться. В рамках функции эти назначения происходят

Например, иногда я сохраняю вычисления просто:

$calcs[$meter['UnitType']['name']] = ...

но в других случаях нужна более конкретная группировка:

$calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] =...

Как видите, иногда он хранится в многомерном массиве, а иногда нет. Я пытался использовать eval (), но безуспешно (не уверен, что это правильный подход). Хранение данных во временной переменной на самом деле не сильно экономит, потому что есть много вложенных циклов и если операторы так массив должен будет повторяться в нескольких местах.

EDIT

Я надеюсь, что следующий пример лучше объясняет мою проблему. Это явно тупая версия:

if(){
     $calcs[$meter['UnitType']['name']] = $data;
} else {
    while () {
       $calcs[$meter['UnitType']['name']] = $data;
    }
} 

Теперь можно использовать ту же логику, но для хранения ее в разных ключах:

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;
    }
} 

Есть ли способ абстрагировать ключи в массиве $ calc [], чтобы у меня была одна функция вместо нескольких функций с разными ключами массива?

Ответы [ 6 ]

0 голосов
/ 06 сентября 2018

Вы можете использовать эту библиотеку , чтобы получить или установить значение в многомерном массиве с использованием массива ключей:

Arr::getNestedElement($calcs, [
    $meter['Resource']['name'], 
    $meter['UnitType']['name'], 
    date('Y-m', $start)
]);

чтобы получить значение или:

Arr::handleNestedElement($calcs, [
    $meter['Resource']['name'], 
    $meter['UnitType']['name'], 
    date('Y-m', $start)
], $data);

для установки $data в качестве значения.

0 голосов
/ 27 октября 2017

Вот функция, которую я написал для установки глубоко вложенных элементов в массивах или объектах:

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;
}

Используя данные вашего примера:

$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);

Результат в виде:1008 *

Второй аргумент dict_set() - это строка $path в точка-нотация .Вы можете построить это, используя динамические ключи с разделителями периода между частями.Функция работает с массивами и объектами.

Она также может добавлять инкрементные элементы к глубоко вложенному массиву, используя [] в качестве элемента $path.Например: parent.child.child.[]

0 голосов
/ 25 августа 2017

Вы можете использовать это, если хотите динамически получать и устанавливать значения массива.

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;
}

Как это работает:

Вызов getVal($data,array('foo','bar','2017-08')) вернет эквивалент$data['foo']['bar']['2017-08'].

Вызов setVal($data,array('foo','bar','2017-08'),'hello') установит значение, как если бы вы позвонили $data['foo']['bar']['2017-08'] = 'hello'.несуществующие ключи будут создаваться автоматически с помощью php magic .

Это может быть полезно, если вы хотите построить структуру массива динамически.

0 голосов
/ 21 февраля 2017

Я согласен с комментарием к OP @Jake N, что, возможно, использование объектов - лучший подход.Тем не менее, если вы хотите использовать массивы, вы можете проверить наличие ключей в условном выражении, например так:

if(
    array_key_exists('Resource', $meter)
) {
    $calcs[$meter['Resource']['name']][$meter['UnitType']['name']][date('Y-m',$start)] = $data;
} else {
    $calcs[$meter['UnitType']['name']] = $data;
}

С другой стороны, если вы хотите использовать объекты, вы можете создать 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>';
}

Очевидно, вы можете пойти дальше, например, проверить наличие ключей массива вконструктор объекта, который дает свойству объекта resource значение по умолчанию, если оно не предоставлено, и т. д., но это начало подхода OO.

0 голосов
/ 25 сентября 2014

Попробуйте использовать корпус переключателя.

<?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;
    }
}
?>
0 голосов
/ 14 сентября 2010

Не будет ли легче сделать следующее

$calcs = array(
    $meter['Resource']['name'] => array(
        $meter['UnitType']['name'] => 'Some Value',
        $meter['UnitType']['name2'] => 'Some Value Again'
    ),
);

или вы можете использовать объекты

$calcs = new stdClass();
$calcs->{$meter['UnitType']['name']} = 'Some Value';

но я бы посоветовал вам построить свою структуру в массивах, а потом делать!

$calcs = (object)$calcs_array;

или вы можете зациклить свой первый массив в новый массив!

$new = array();
$d = date('Y-m',$start);
foreach($meter as $key => $value)
{
    $new[$key]['name'][$d] = array();
}

Отдай это назад и посмотри, как получается структура массива.

...