如何写getter / setter通过键名来访问多级数组?
我必须在PHP中实现一个setter,它允许我指定一个数组(目标)的键或子键,并将名称作为点分隔键值传递。
给出以下代码:
$arr = array('a' => 1, 'b' => array( 'y' => 2, 'x' => array('z' => 5, 'w' => 'abc') ), 'c' => null); $key = 'bxz'; $path = explode('.', $key);
从$key
的值我想达到$arr['b']['x']['z']
的值5 。
现在….给出一个$key
的variables值和一个不同的$arr
值(具有不同的深度)。
我如何设置$key
指向的元素的值?
getter get()
我写了这个代码:
public static function get($name, $default = null) { $setting_path = explode('.', $name); $val = $this->settings; foreach ($setting_path as $key) { if(array_key_exists($key, $val)) { $val = $val[$key]; } else { $val = $default; break; } } return $val; }
编写setter比较困难,因为我成功地从$key
到达了正确的元素,但是我不能在原始数组中设置这个值,也不知道如何一次性指定这个key。
我应该使用某种回溯? 或者我可以避免它?
假设$path
已经是一个explode()
(或添加到函数)数组,然后使用引用。 你需要添加一些错误检查的情况下,无效的$path
等…
$key = 'bxz'; $path = explode('.', $key);
消气
function get($path, $array) { //$path = explode('.', $path); //if needed $temp = &$array; foreach($path as $key) { $temp =& $temp[$key]; } return $temp; } $value = get($path, $arr); //returns NULL if the path doesn't exist
Setter / Creator
这个组合将在现有的数组中设置一个值,或者如果你传递一个尚未定义的数组,就创build这个数组。 确保定义$array
以通过引用传递&$array
:
function set($path, &$array=array(), $value=null) { //$path = explode('.', $path); //if needed $temp = &$array; foreach($path as $key) { $temp =& $temp[$key]; } $temp = $value; } set($path, $arr); //or set($path, $arr, 'some value');
*原来的答案有一些有限的function,我将离开,以防他们对某人有用:
二传手
确保定义$array
以通过引用传递&$array
:
function set(&$array, $path, $value) { //$path = explode('.', $path); //if needed $temp = &$array; foreach($path as $key) { $temp =& $temp[$key]; } $temp = $value; } set($arr, $path, 'some value');
或者如果你想返回更新的数组(因为我很无聊):
function set($array, $path, $value) { //$path = explode('.', $path); //if needed $temp = &$array; foreach($path as $key) { $temp =& $temp[$key]; } $temp = $value; return $array; } $arr = set($arr, $path, 'some value');
创造者
如果您不想创build数组并可以设置值:
function create($path, $value=null) { //$path = explode('.', $path); //if needed foreach(array_reverse($path) as $key) { $value = array($key => $value); } return $value; } $arr = create($path); //or $arr = create($path, 'some value');
为了娱乐
构造和评估类似$array['b']['x']['z'];
给一个stringbxz
:
function get($array, $path) { //$path = explode('.', $path); //if needed $path = "['" . implode("']['", $path) . "']"; eval("\$result = \$array{$path};"); return $result; }
我没有在纯PHP的解决scheme,但具体使用ouzo好吃的 Arrays :: getNestedValue方法:
$arr = array('a' => 1, 'b' => array( 'y' => 2, 'x' => array('z' => 5, 'w' => 'abc') ), 'c' => null); $key = 'bxz'; $path = explode('.', $key); print_r(Arrays::getNestedValue($arr, $path));
同样,如果你需要设置嵌套值,你可以使用Arrays :: setNestedValue方法。
$arr = array('a' => 1, 'b' => array( 'y' => 2, 'x' => array('z' => 5, 'w' => 'abc') ), 'c' => null); Arrays::setNestedValue($arr, array('d', 'e', 'f'), 'value'); print_r($arr);
我有一个我经常使用的工具,我会分享。 不同之处在于它使用数组访问表示法(例如b[x][z]
)而不是点表示法(例如bxz
)。 有了文档和代码,它是不言自明的。
<?php class Utils { /** * Gets the value from input based on path. * Handles objects, arrays and scalars. Nesting can be mixed. * Eg: $input->a->b->c = 'val' or $input['a']['b']['c'] = 'val' will * return "val" with path "a[b][c]". * @see Utils::arrayParsePath * @param mixed $input * @param string $path * @param mixed $default Optional default value to return on failure (null) * @return NULL|mixed NULL on failure, or the value on success (which may also be NULL) */ public static function getValueByPath($input,$path,$default=null) { if ( !(isset($input) && (static::isIterable($input) || is_scalar($input))) ) { return $default; // null already or we can't deal with this, return early } $pathArray = static::arrayParsePath($path); $last = &$input; foreach ( $pathArray as $key ) { if ( is_object($last) && property_exists($last,$key) ) { $last = &$last->$key; } else if ( (is_scalar($last) || is_array($last)) && isset($last[$key]) ) { $last = &$last[$key]; } else { return $default; } } return $last; } /** * Parses an array path like a[b][c] into a lookup array like array('a','b','c') * @param string $path * @return array */ public static function arrayParsePath($path) { preg_match_all('/\\[([^[]*)]/',$path,$matches); if ( isset($matches[1]) ) { $matches = $matches[1]; } else { $matches = array(); } preg_match('/^([^[]+)/',$path,$name); if ( isset($name[1]) ) { array_unshift($matches,$name[1]); } else { $matches = array(); } return $matches; } /** * Check if a value/object/something is iterable/traversable, * eg can it be run through a foreach? * Tests for a scalar array (is_array), an instance of Traversable, and * and instance of stdClass * @param mixed $value * @return boolean */ public static function isIterable($value) { return is_array($value) || $value instanceof Traversable || $value instanceof stdClass; } } $arr = array('a' => 1, 'b' => array( 'y' => 2, 'x' => array('z' => 5, 'w' => 'abc') ), 'c' => null); $key = 'b[x][z]'; var_dump(Utils::getValueByPath($arr,$key)); // int 5 ?>
如果数组的键是唯一的,可以使用array_walk_recursive在几行代码中解决问题:
$arr = array('a' => 1, 'b' => array( 'y' => 2, 'x' => array('z' => 5, 'w' => 'abc') ), 'c' => null); function changeVal(&$v, $key, $mydata) { if($key == $mydata[0]) { $v = $mydata[1]; } } $key = 'z'; $value = '56'; array_walk_recursive($arr, 'changeVal', array($key, $value)); print_r($arr);
作为一个“吸气剂”,我过去曾经使用过这种方法:
$array = array('data' => array('one' => 'first', 'two' => 'second')); $key = 'data.one'; function find($key, $array) { $parts = explode('.', $key); foreach ($parts as $part) { $array = $array[$part]; } return $array; } $result = find($key, $array); var_dump($result);
我有一个非常简单和肮脏的解决scheme( 真的很脏!不要使用,如果密钥的价值是不可信的! )。 这可能比循环数组更有效。
function array_get($key, $array) { return eval('return $array["' . str_replace('.', '"]["', $key) . '"];'); } function array_set($key, &$array, $value=null) { eval('$array["' . str_replace('.', '"]["', $key) . '"] = $value;'); }
这两个函数都在代码片段上进行eval
,将密钥转换为PHP代码的数组元素。 并返回或设置相应的键的数组值。
这个函数和接受的答案一样,加上一个引用的第三个参数,如果键存在的话,它被设置为true / false
function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) { $ref = &$array; foreach ($parents as $parent) { if (is_array($ref) && array_key_exists($parent, $ref)) { $ref = &$ref[$parent]; } else { $key_exists = FALSE; $null = NULL; return $null; } } $key_exists = TRUE; return $ref; }