php - studio - ¿Cómo acceder y manipular una matriz multidimensional por nombres de clave/ruta?
php getter setter generator (8)
Tengo que implementar un setter en PHP, que me permite especificar la clave, o subclave, de una matriz (el objetivo), pasando el nombre como un valor de claves separadas por puntos.
Dado el siguiente código:
$arr = array(''a'' => 1,
''b'' => array(
''y'' => 2,
''x'' => array(''z'' => 5, ''w'' => ''abc'')
),
''c'' => null);
$key = ''b.x.z'';
$path = explode(''.'', $key);
Desde el valor de
$key
quiero alcanzar el valor
5
de
$arr[''b''][''x''][''z'']
.
Ahora, dado un valor variable de
$key
y un valor diferente de
$arr
(con diferente profundidad).
¿Cómo puedo establecer el valor del elemento referido por
$key
?
Para el
getter
get()
escribí este código:
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;
}
Escribir un
setter
es más difícil porque logro alcanzar el elemento correcto (desde la
$key
), pero no puedo establecer el valor en la matriz original y no sé cómo especificar las claves de una vez.
¿Debo usar algún tipo de retroceso? ¿O puedo evitarlo?
Aquí un código simple para acceder y manipular la matriz MD. Pero no hay valores.
setter:
eval(''$vars = &$array["'' . implode(''"]["'', explode(''.'', strtolower($dot_seperator_path))) . ''"];'');
$vars = $new_value;
adquiridor:
eval(''$vars = $array["'' . implode(''"]["'', explode(''.'', strtolower($dot_seperator_path))) . ''"];'');
return $vars;
Como "getter", he usado esto en el pasado:
$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);
Esta función hace lo mismo que la respuesta aceptada, además se agrega un tercer parámetro por referencia que se establece en verdadero / falso si la clave está presente
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;
}
No tengo una solución para usted en PHP puro, sino que Arrays::getNestedValue objetos ouzo Arrays::getNestedValue método:
$arr = array(''a'' => 1,
''b'' => array(
''y'' => 2,
''x'' => array(''z'' => 5, ''w'' => ''abc'')
),
''c'' => null);
$key = ''b.x.z'';
$path = explode(''.'', $key);
print_r(Arrays::getNestedValue($arr, $path));
Del mismo modo, si necesita establecer un valor anidado, puede usar el método 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);
Si las claves de la matriz son únicas, puede resolver el problema en unas pocas líneas de código usando 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);
Suponiendo que
$path
ya es una matriz a través de
explode
(o agregar a la función), puede usar referencias.
isset
agregar alguna comprobación de errores en caso de
$path
no válido
$path
etc. (think
isset
):
$key = ''b.x.z'';
$path = explode(''.'', $key);
Adquiridor
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 / Creador
Esta combinación establecerá un valor en una matriz existente o creará la matriz si pasa una que aún no se ha definido.
Asegúrese de definir
$array
para pasar por referencia
&$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'');
Unsetter
Esto
unset
la clave final en la ruta:
function unsetter($path, &$array) {
//$path = explode(''.'', $path); //if needed
$temp =& $array;
foreach($path as $key) {
if(!is_array($temp[$key])) {
unset($temp[$key]);
} else {
$temp =& $temp[$key];
}
}
}
unsetter($path, $arr);
* La respuesta original tenía algunas funciones limitadas que dejaré en caso de que sean de utilidad para alguien:
Setter
Asegúrese de definir
$array
para pasar por referencia
&$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'');
O si desea devolver la matriz actualizada (porque estoy aburrido):
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'');
Creador
Si desea crear la matriz y, opcionalmente, establecer el valor:
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'');
Por diversión
Construye y evalúa algo como
$array[''b''][''x''][''z''];
dada una cadena
bxz
:
function get($array, $path) {
//$path = explode(''.'', $path); //if needed
$path = "[''" . implode("''][''", $path) . "'']";
eval("/$result = /$array{$path};");
return $result;
}
Tengo una solución realmente simple y sucia (¡ realmente sucia! ¡NO la use si el valor de la clave no es confiable! ). Puede ser más eficiente que recorrer la matriz.
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;'');
}
Ambas funciones realizan una
eval
en un fragmento de código donde la clave se convierte en un elemento de la matriz como código PHP.
Y devuelve o establece el valor de la matriz en la clave correspondiente.
Tengo una utilidad que uso regularmente que compartiré.
La diferencia es que utiliza la notación de acceso a la matriz (por ejemplo,
b[x][z]
) en lugar de la notación de puntos (por ejemplo,
bxz
).
Con la documentación y el código, se explica por sí mismo.
<?php
class Utils {
/**
* Gets the value from input based on path.
* Handles objects, arrays and scalars. Nesting can be mixed.
* E.g.: $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,
* e.g. 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
?>