I have a multidimensional array, here is a small excerpt:
Array (
[Albums] => Array (
[A Great Big World - Is There Anybody Out There] => Array(...),
[ATB - Contact] => Array(...),
)
[Pop] => Array (...)
)
And I have a dynamic path:
/albums/a_great_big_world_-_is_there_anybody_out_there
What would be the best way to retrieve the value of (in this example) $arr["albums"]["A Great Big World - Is There Anybody Out There"]?
Please note that it should be dynamic, since the nesting can go deeper than the 2 levels in this example.
EDIT
Here is the function I use to create a simple string for the URL:
function formatURL($url) {
return preg_replace('/__+/', '_', preg_replace('/[^a-z0-9_\s-]/', "", strtolower(str_replace(" ", "_", $url))));
}
$array = array(...);
$path = '/albums/a_great_big_world_-_is_there_anybody_out_there';
$value = $array;
foreach (explode('/', trim($path, '/')) as $key) {
if (isset($value[$key]) && is_array($value[$key])) {
$value = $value[$key];
} else {
throw new Exception("Path $path is invalid");
}
}
echo $value;
Related
Let say I have an array like:
Array
(
[0] => Array
(
[Data] => Array
(
[id] => 1
[title] => Manager
[name] => John Smith
)
)
[1] => Array
(
[Data] => Array
(
[id] => 1
[title] => Clerk
[name] =>
(
[first] => Jane
[last] => Smith
)
)
)
)
I want to be able to build a function that I can pass a string to that will act as the array index path and return the appropriate array value without using eval(). Is that possible?
function($indexPath, $arrayToAccess)
{
// $indexPath would be something like [0]['Data']['name'] which would return
// "Manager" or it could be [1]['Data']['name']['first'] which would return
// "Jane" but the amount of array indexes that will be in the index path can
// change, so there might be 3 like the first example, or 4 like the second.
return $arrayToAccess[$indexPath] // <- obviously won't work
}
A Bit later, but... hope helps someone:
// $pathStr = "an:string:with:many:keys:as:path";
$paths = explode(":", $pathStr);
$itens = $myArray;
foreach($paths as $ndx){
$itens = $itens[$ndx];
}
Now itens is the part of the array you wanted to.
[]'s
Labs
you might use an array as path (from left to right), then a recursive function:
$indexes = {0, 'Data', 'name'};
function get_value($indexes, $arrayToAccess)
{
if(count($indexes) > 1)
return get_value(array_slice($indexes, 1), $arrayToAccess[$indexes[0]]);
else
return $arrayToAccess[$indexes[0]];
}
This is an old question but it has been referenced as this question comes up frequently.
There are recursive functions but I use a reference:
function array_nested_value($array, $path) {
$temp = &$array;
foreach($path as $key) {
$temp =& $temp[$key];
}
return $temp;
}
$path = array(0, 'Data', 'Name');
$value = array_nested_value($array, $path);
Try the following where $indexPath is formatted like a file path i.e.
'<array_key1>/<array_key2>/<array_key3>/...'.
function($indexPath, $arrayToAccess)
{
$explodedPath = explode('/', $indexPath);
$value =& $arrayToAccess;
foreach ($explodedPath as $key) {
$value =& $value[$key];
}
return $value;
}
e.g. using the data from the question, $indexPath = '1/Data/name/first' would return $value = Jane.
function($indexPath, $arrayToAccess)
{
eval('$return=$arrayToAccess'.$indexPath.';');
return $return;
}
You have to parse indexPath string. Chose some separator (for example "."), read text until "." that would be the first key, then read rest until next, that would be next key. Do that until no more dots.
You ken store key in array. Do foreach loop on this array to get seeked element.
Here is one way to get the job done, if string parsing is the way you want to go.
$data[0]["Data"]["stuff"] = "cake";
$path = "[0][\"Data\"]['stuff']";
function indexPath($path,$array){
preg_match_all("/\[['\"]*([a-z0-9_-]+)['\"]*\]/i",$path,$matches);
if(count($matches[1]) > 0) {
foreach ($matches[1] as $key) {
if (isset($array[$key])) {
$array = $array[$key];
} else {
return false;
}
}
} else {
return false;
}
return $array;
}
print_r(indexPath($path,$data));
A preg_match_all, cycling through the matched results would give you CLOSE to the result you wanted. You need to be careful with all of the strategies listed here for lost information. For instance, you have to devise some way to ensure that 55 stays as type int and isn't parsed as type string.
In addition to AbraCadaver:
function array_nested_value($array, $path) {
foreach($path as $key) {
$array = $array[$key];
}
return $array;
}
$path = array(0, 'Data', 'Name');
$value = array_nested_value($array, $path);
Possible use
function get_array_value($array=array(), $path=array()){
foreach($path as $key) {
if(isset($array[$key])){
$array=$array[$key];
}
else{
$array=NULL;
break;
}
}
return $array;
}
function custom_isset($array=array(), $path=array()){
$isset=true;
if(is_array($array)&&is_null(get_array_value($array, $path))){
$isset=false;
}
return $isset;
}
function is($array=array(), $path=array()){
$is=false;
if(is_array($array)){
$array_value=get_array_value($array, $path);
if(is_bool($array_value)){
$is=$array_value;
}
}
return $is;
}
Example
$status=[];
$status['updated']=true;
if(is($status,array('updated'))){
//do something...
}
Resources
https://gist.github.com/rafasashi/0d3ebadf08b8c2976a9d
If you already know the exact array element that you are pulling out why write a function to do it? What's wrong with just
$array[0]['data']['title']
Let say I have an array like:
Array
(
[0] => Array
(
[Data] => Array
(
[id] => 1
[title] => Manager
[name] => John Smith
)
)
[1] => Array
(
[Data] => Array
(
[id] => 1
[title] => Clerk
[name] =>
(
[first] => Jane
[last] => Smith
)
)
)
)
I want to be able to build a function that I can pass a string to that will act as the array index path and return the appropriate array value without using eval(). Is that possible?
function($indexPath, $arrayToAccess)
{
// $indexPath would be something like [0]['Data']['name'] which would return
// "Manager" or it could be [1]['Data']['name']['first'] which would return
// "Jane" but the amount of array indexes that will be in the index path can
// change, so there might be 3 like the first example, or 4 like the second.
return $arrayToAccess[$indexPath] // <- obviously won't work
}
A Bit later, but... hope helps someone:
// $pathStr = "an:string:with:many:keys:as:path";
$paths = explode(":", $pathStr);
$itens = $myArray;
foreach($paths as $ndx){
$itens = $itens[$ndx];
}
Now itens is the part of the array you wanted to.
[]'s
Labs
you might use an array as path (from left to right), then a recursive function:
$indexes = {0, 'Data', 'name'};
function get_value($indexes, $arrayToAccess)
{
if(count($indexes) > 1)
return get_value(array_slice($indexes, 1), $arrayToAccess[$indexes[0]]);
else
return $arrayToAccess[$indexes[0]];
}
This is an old question but it has been referenced as this question comes up frequently.
There are recursive functions but I use a reference:
function array_nested_value($array, $path) {
$temp = &$array;
foreach($path as $key) {
$temp =& $temp[$key];
}
return $temp;
}
$path = array(0, 'Data', 'Name');
$value = array_nested_value($array, $path);
Try the following where $indexPath is formatted like a file path i.e.
'<array_key1>/<array_key2>/<array_key3>/...'.
function($indexPath, $arrayToAccess)
{
$explodedPath = explode('/', $indexPath);
$value =& $arrayToAccess;
foreach ($explodedPath as $key) {
$value =& $value[$key];
}
return $value;
}
e.g. using the data from the question, $indexPath = '1/Data/name/first' would return $value = Jane.
function($indexPath, $arrayToAccess)
{
eval('$return=$arrayToAccess'.$indexPath.';');
return $return;
}
You have to parse indexPath string. Chose some separator (for example "."), read text until "." that would be the first key, then read rest until next, that would be next key. Do that until no more dots.
You ken store key in array. Do foreach loop on this array to get seeked element.
Here is one way to get the job done, if string parsing is the way you want to go.
$data[0]["Data"]["stuff"] = "cake";
$path = "[0][\"Data\"]['stuff']";
function indexPath($path,$array){
preg_match_all("/\[['\"]*([a-z0-9_-]+)['\"]*\]/i",$path,$matches);
if(count($matches[1]) > 0) {
foreach ($matches[1] as $key) {
if (isset($array[$key])) {
$array = $array[$key];
} else {
return false;
}
}
} else {
return false;
}
return $array;
}
print_r(indexPath($path,$data));
A preg_match_all, cycling through the matched results would give you CLOSE to the result you wanted. You need to be careful with all of the strategies listed here for lost information. For instance, you have to devise some way to ensure that 55 stays as type int and isn't parsed as type string.
In addition to AbraCadaver:
function array_nested_value($array, $path) {
foreach($path as $key) {
$array = $array[$key];
}
return $array;
}
$path = array(0, 'Data', 'Name');
$value = array_nested_value($array, $path);
Possible use
function get_array_value($array=array(), $path=array()){
foreach($path as $key) {
if(isset($array[$key])){
$array=$array[$key];
}
else{
$array=NULL;
break;
}
}
return $array;
}
function custom_isset($array=array(), $path=array()){
$isset=true;
if(is_array($array)&&is_null(get_array_value($array, $path))){
$isset=false;
}
return $isset;
}
function is($array=array(), $path=array()){
$is=false;
if(is_array($array)){
$array_value=get_array_value($array, $path);
if(is_bool($array_value)){
$is=$array_value;
}
}
return $is;
}
Example
$status=[];
$status['updated']=true;
if(is($status,array('updated'))){
//do something...
}
Resources
https://gist.github.com/rafasashi/0d3ebadf08b8c2976a9d
If you already know the exact array element that you are pulling out why write a function to do it? What's wrong with just
$array[0]['data']['title']
I have a multidimensional array, here is a small excerpt:
Array (
[Albums] => Array (
[A Great Big World - Is There Anybody Out There] => Array(...),
[ATB - Contact] => Array(...),
)
[Pop] => Array (...)
)
And I have a dynamic path:
/albums/a_great_big_world_-_is_there_anybody_out_there
What would be the best way to retrieve the value of (in this example) $arr["albums"]["A Great Big World - Is There Anybody Out There"]?
Please note that it should be dynamic, since the nesting can go deeper than the 2 levels in this example.
EDIT
Here is the function I use to create a simple string for the URL:
function formatURL($url) {
return preg_replace('/__+/', '_', preg_replace('/[^a-z0-9_\s-]/', "", strtolower(str_replace(" ", "_", $url))));
}
$array = array(...);
$path = '/albums/a_great_big_world_-_is_there_anybody_out_there';
$value = $array;
foreach (explode('/', trim($path, '/')) as $key) {
if (isset($value[$key]) && is_array($value[$key])) {
$value = $value[$key];
} else {
throw new Exception("Path $path is invalid");
}
}
echo $value;
Example:
$arr = array(array("name"=>"Bob","species"=>"human","children"=>array(array("name"=>"Alice","age"=>10),array("name"=>"Jane","age"=>13)),array("name"=>"Sparky","species"=>"dog")));
print_r($arr);
array_walk_recursive($arr, function($v,$k) {
echo "key: $k\n";
});
The thing here is that I get only the last key, but I have no way to refer where I been, that is to store a particular key and change value after I left the function or change identical placed value in another identical array.
What I would have to get instead of string is an array that would have all keys leading to given value, for example [0,"children",1,"age"].
Edit:
This array is only example. I've asked if there is universal way to iterate nested array in PHP and get full location path not only last key. And I know that there is a way of doing this by creating nested loops reflecting structure of the array. But I repeat: I don't know the array structure in advance.
To solve your problem you will need recursion. The following code will do what you want, it will also find multiple paths if they exists:
$arr = array(
array(
"name"=>"Bob",
"species"=>"human",
"children"=>array(
array(
"name"=>"Alice",
"age"=>10
),
array(
"name"=>"Jane",
"age"=>13
)
),
array(
"name"=>"Sparky",
"species"=>"dog"
)
)
);
function getPaths($array, $search, &$paths, $currentPath = array()) {
foreach ($array as $key => $value) {
if (is_array($value)) {
$currentPath[] = $key;
if (true !== getPaths($value, $search, $paths, $currentPath)) {
array_pop($currentPath);
}
} else {
if ($search == $value) {
$currentPath[] = $key;
$paths[] = $currentPath;
return true;
}
}
}
}
$paths = array();
getPaths($arr, 13, $paths);
print_r($paths);
Hey guys!
I need some help writing a code that creates an array in the format I needed in. I have this long string of text like this -> "settings=2&options=3&color=3&action=save"...etc Now the next thing I did to make it into an array is the following:
$form_data = explode("&", $form_data);
Ok so far so good...I now have an array like so:
Array
(
[0] => settings=2
[1] => options=3
[2] => color=3
[3] => action=save
)
1
Ok now I need to know how to do two things. First, how can I remove all occurences of "action=save" from the array?
Second, how can I make this array become a key value pair (associative array)? Like "settings=>2"?
Thanks...
There's a function for that. :)
parse_str('settings=2&options=3&color=3&action=save', $arr);
if (isset($arr['action']) && $arr['action'] == 'save') {
unset($arr['action']);
}
print_r($arr);
But just for reference, you could do it manually like this:
$str = 'settings=2&options=3&color=3&action=save';
$arr = array();
foreach (explode('&', $str) as $part) {
list($key, $value) = explode('=', $part, 2);
if ($key == 'action' && $value == 'save') {
continue;
}
$arr[$key] = $value;
}
This is not quite equivalent to parse_str, since key[] keys wouldn't be parsed correctly. I'd be sufficient for your example though.
$str = 'settings=2&options=3&color=3&action=save&action=foo&bar=save';
parse_str($str, $array);
$key = array_search('save', $array);
if($key == 'action') {
unset($array['action']);
}
Ideone Link
This could help on the parsing your array, into key/values
$array; // your array here
$new_array = array();
foreach($array as $key)
{
$val = explode('=',$key);
// could also unset($array['action']) if that's a global index you want removed
// if so, no need to use the if/statement below -
// just the setting of the new_array
if(($val[0] != 'action' && $val[1] != 'save'))$new_array[] = array($val[0]=>$val[1]);
}