【问题标题】:How to dynamically set array keys in php如何在php中动态设置数组键
【发布时间】:2011-04-11 21:05:09
【问题描述】:

我有一些用于对数据进行排序的逻辑,但根据用户输入,数据的分组方式不同。现在我有五个不同的函数,它们包含相同的逻辑但不同的分组。有没有办法组合这些函数并动态设置一个可以正确分组的值。在函数中,这些分配正在发生

例如,有时我只是通过以下方式存储计算:

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

但其他时候需要更具体的分组:

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

如您所见,有时它存储在多维数组中,有时则不存储。我一直在尝试使用 eval() 但没有成功(不确定这是正确的方法)。将数据存储在临时变量中并不能真正节省太多,因为有许多嵌套循环和 if 语句,因此必须在多个位置重复该数组。

编辑

我希望下面的例子能更好地解释我的问题。这显然是一个愚蠢的版本:

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[] 数组中的键,这样我就可以拥有一个函数,而不是拥有多个具有不同数组键的函数?

【问题讨论】:

  • 使用对象可以更好地管理吗?
  • 我真的不明白这个问题。您是否尝试以统一的方式查询这些数据结构?
  • 我进行了编辑以帮助解释。希望它确实有所帮助。
  • 你能提供一些示例数据吗?您是在尝试确定键的名称是 UnitType 还是 Resource,还是 UnitType 和 Resource 实际上将动态内容存储在键中?
  • @Kramer,为用户输入的值添加占位符。使您的数组统一。

标签: php arrays multidimensional-array eval


【解决方案1】:

如果您想动态获取和设置数组值,可以使用它。

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 会自动创建不存在的密钥。

如果您想动态构建数组的结构,这会很有用。

【讨论】:

    【解决方案2】:

    这是我为在数组或对象上设置深度嵌套成员而编写的一个函数:

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

    输出结果如下:

    Array
    (
        [resource1] => Array
            (
                [unit1] => Array
                    (
                        [2017-10] => value1
                    )
    
                [unit2] => Array
                    (
                        [2017-11] => value2
                    )
    
            )
    
        [resource2] => Array
            (
                [unit1] => Array
                    (
                        [2017-10] => value3
                    )
    
            )
    
    )
    

    dict_set() 的第二个参数是 dot-notation 中的 $path 字符串。您可以使用动态键和部件之间的句点分隔符来构建它。该函数适用于数组和对象。

    它还可以通过使用[] 作为$path 的元素将增量成员附加到深度嵌套的数组中。例如:parent.child.child.[]

    【讨论】:

      【解决方案3】:

      做以下事情会不会更容易

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

      先给它,看看数组结构是怎么出来的。

      【讨论】:

      • 我不确定问题是否明确。我做了一些修改,试图帮助解释。
      【解决方案4】:

      尝试使用开关盒。

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

      【讨论】:

        【解决方案5】:

        我同意@Jake N 对 OP 的评论,即也许使用对象是一种更好的方法。尽管如此,如果您想使用数组,您可以检查条件中是否存在键,如下所示:

        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,则为其赋予默认值等等,但这是面向对象方法的开始.

        【讨论】:

          【解决方案6】:

          您可以使用this library 通过键数组获取或设置多维数组中的值:

          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 设置为值。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-06-10
            • 2020-01-31
            • 2016-12-16
            • 1970-01-01
            • 2015-06-10
            • 2010-10-15
            • 1970-01-01
            相关资源
            最近更新 更多