PHP parsing multidimensional json array of any depth based on key - php

I have an json array like given below, what I want is to
parse through the array and get a value for corresponding
key .Eg SubAdministrativeAreaName .I could have parsed it
like .
["AddressDetails"]['Country']['AdministrativeArea'] ['SubAdministrativeArea']['SubAdministrativeAreaName']
but the structure of array is not fixed , it may contain some other
keys within which "SubAdmininstrativeArea" may be enclosed .
What I want is a php function that will search for a particular key
name through multidimensional json array of any depth .
Any help would be appreciated .
"AddressDetails": {
"Accuracy": 6,
"Country": {
"AdministrativeArea": {
"AdministrativeAreaName": "Maharashtra",
"SubAdministrativeArea": {
"SubAdministrativeAreaName": "Parbhani",
"Thoroughfare": {
"ThoroughfareName": "SH217"
}
}
},
"CountryName": "India",
"CountryNameCode": "IN"
}
}

OK, different answer based on comment below:
function array_key_search_deep($needle, $haystack) {
$value = NULL;
if(isset($haystack[$needle])) {
$value = $haystack[$needle];
} else {
foreach($haystack as $node) {
if(is_array($node)) {
$value = array_key_search_deep($needle, $node);
if(!is_null($value)) {
break;
}
}
}
}
return $val;
}
Old Answer
This will allow you to traverse an unknown path in any array tree:
$path = array('AddressDetails', 'Country', 'AdministrativeArea', 'SubAdministrativeArea', 'SubAdministrativeAreaName');
$node = $json_object;
foreach($path as $path_index) {
if(isset($node[$path_index])) {
$node = $node[$path_index];
} else {
$node = NULL;
}
}
echo($node);

Thanks guys , what I made was a a solution like this
I finally found a simple solution myself
For eg) To get "ThoroughfareName" make a call
recursive_array_search($json_array,'ThoroughfareName') ;
function recursive_array_search($arr,$jackpot)
{
foreach ($arr as $key => $value)
{
if(is_array($value))
{
$val=recursive_array_search($value,$jackpot) ;
return $val;
}
else
{
if($key==$jackpot)
return $value;
}
}
}

You could use JSONPath (XPath for JSON)
http://goessner.net/articles/JsonPath/
(with for instance the expression "$..SubAdministrativeAreaName")
EDIT: Haven't tested it, not sure how reliable it is.

Related

Illegal Offset Type Error

So I've got the following function:
function sitesync_empty_vals(&$entity) {
$vals = false;
$entity = (array) $entity;
foreach ($entity as $field) {
if (is_array($field)) {
foreach ($field as $lang) {
foreach ($lang as $item) {
if (isset($item['value'])) {
if (empty($item['value'])) {
unset($field[$lang][$item]);
break;
}
else {
$vals = true;
}
}
}
}
if (!$vals && is_array($lang)) {
watchdog("field", print_r($field, true));
unset($field[$lang]);
}
}
}
}
I keep getting the error Illegal offset type.
I'm not quite understanding why I'd be getting this error - it seems to be related to unsetting the $field[$lang][$item] (I don't get the error when I comment it out), but why would that be? Is it because it's trying to iterate over that item after it is unset? In the case that that particular value is empty, I want to unset the whole $item - this is for data normalization between two different servers, one of which doesn't store any data, and one of which stores the data as a 0.
foreach ($lang as $item) {
^^^^---array
unset($field[$lang][$item]);
^^^^^---using Array as array key
You probably want something more like:
foreach($lang as $lang_key => $item) {
unset($field[$lang_key]....);
instead. And as the comments below have noted, $item is ALSO an array, so you'll want a similar treatment for that as well.
Here is a quote from the PHP Docs:
Arrays and objects can not be used as keys. Doing so will result in a warning: Illegal offset type.
http://www.php.net/manual/en/language.types.array.php
You have to use the keys, not the arrays themselves:
function sitesync_empty_vals(&$entity) {
$vals = false;
$entity = (array) $entity;
foreach ($entity as $field) {
if (is_array($field)) {
foreach ($field as $langKey=>$lang) {
foreach ($lang as $itemKey=>$item) {
if (isset($item['value'])) {
if (empty($item['value'])) {
unset($field[$langKey][$itemKey]);
break;
}
else {
$vals = true;
}
}
}
}
if (!$vals && is_array($lang)) {
watchdog("field", print_r($field, true));
unset($field[$langKey]);
}
}
}
}

PHP Function that can return value from an array key a dynamic number of levels deep

Using PHP, I would like to write a function that accomplishes what is shown by this pseudo code:
function return_value($input_string='array:subArray:arrayKey')
{
$segments = explode(':',$input_string);
$array_depth = count(segments) - 1;
//Now the bit I'm not sure about
//I need to dynamically generate X number of square brackets to get the value
//So that I'm left with the below:
return $array[$subArray][$arrayKey];
}
Is the above possible? I'd really appreciate some pointer on how to acheive it.
You can use a recursive function (or its iterative equivalent since it's tail recursion):
function return_value($array, $input_string) {
$segments = explode(':',$input_string);
// Can we go next step?
if (!array_key_exists($segments[0], $array)) {
return false; // cannot exist
}
// Yes, do so.
$nextlevel = $array[$segments[0]];
if (!is_array($nextlevel)) {
if (1 == count($segments)) {
// Found!
return $nextlevel;
}
// We can return $nextlevel, which is an array. Or an error.
return false;
}
array_shift($segments);
$nextsegments = implode(':', $segments);
// We can also use tail recursion here, enclosing the whole kit and kaboodle
// into a loop until $segments is empty.
return return_value($nextlevel, $nextsegments);
}
Passing one object
Let's say we want this to be an API and pass only a single string (please remember that HTTP has some method limitation in this, and you may need to POST the string instead of GET).
The string would need to contain both the array data and the "key" location. It's best if we send first the key and then the array:
function decodeJSONblob($input) {
// Step 1: extract the key address. We do this is a dirty way,
// exploiting the fact that a serialized array starts with
// a:<NUMBEROFITEMS>:{ and there will be no "{" in the key address.
$n = strpos($input, ':{');
$items = explode(':', substr($input, 0, $n));
// The last two items of $items will be "a" and "NUMBEROFITEMS"
$ni = array_pop($items);
if ("a" != ($a = array_pop($items))) {
die("Something strange at offset $n, expecting 'a', found {$a}");
}
$array = unserialize("a:{$ni}:".substr($input, $n+1));
while (!empty($items)) {
$key = array_shift($items);
if (!array_key_exists($key, $array)) {
// there is not this item in the array.
}
if (!is_array($array[$key])) {
// Error.
}
$array = $array[$key];
}
return $array;
}
$arr = array(
0 => array(
'hello' => array(
'joe','jack',
array('jill')
)));
print decodeJSONblob("0:hello:1:" . serialize($arr));
print decodeJSONblob("0:hello:2:0" . serialize($arr));
returns
jack
jill
while asking for 0:hello:2: would get you an array { 0: 'jill' }.
you could use recursion and array_key_exists to walk down to the level of said key.
function get_array_element($key, $array)
{
if(stripos(($key,':') !== FALSE) {
$currentKey = substr($key,0,stripos($key,':'));
$remainingKeys = substr($key,stripos($key,':')+1);
if(array_key_exists($currentKey,$array)) {
return ($remainingKeys,$array[$currentKey]);
}
else {
// handle error
return null;
}
}
elseif(array_key_exists($key,$array)) {
return $array[$key];
}
else {
//handle error
return null;
}
}
Use a recursive function like the following or a loop using references to array keys
<?php
function lookup($array,$lookup){
if(!is_array($lookup)){
$lookup=explode(":",$lookup);
}
$key = array_shift($lookup);
if(!isset($array[$key])){
//throw exception if key is not found so false values can also be looked up
throw new Exception("Key does not exist");
}else{
$val = $array[$key];
if(count($lookup)){
return lookup($val,$lookup);
}
return $val;
}
}
$config = array(
'db'=>array(
'host'=>'localhost',
'user'=>'user',
'pass'=>'pass'
),
'data'=>array(
'test1'=>'test1',
'test2'=>array(
'nested'=>'foo'
)
)
);
echo "Host: ".lookup($config,'db:host')."\n";
echo "User: ".lookup($config,'db:user')."\n";
echo "More levels: ".lookup($config,'data:test2:nested')."\n";
Output:
Host: localhost
User: user
More levels: foo

Faster way to see if an array contains values besides the one specified

I have an array where each element has a subarray with multiple Ids. When looping through the array, I'd like to check if the subarray has any elements besides a given one.
For example, I'd like to echo 'Yes' whenever one of the subarrays has any ids other than 'TESTID'.
I can do this by looping through the subarray, but I'd like to know of a way that doesn't require double loops.
Here's the current code:
foreach ($elements as $element) {
...
if (besidesInArray('TESTID',$element['ids'])) {
//operations
} else {
//element only has 'TESTID'
}
...
}
...
function besidesInArray($needle, $haystack) {
foreach ($haystack as $hay) {
if($hay != $needle) {
return TRUE;
}
}
return FALSE;
}
While this code works, I'd like to see if there's a more elegant solution.
You can use in_array() function to achieve this
foreach($array as $key => $subarray)
{
if(in_array("TESTID", $subarray))
{
//found
} else {
//not found
}
}
preg_grep for TESTID but invert the grep so that it returns entries NOT matching.
foreach($array as $subarray) {
if(preg_grep("/TESTID/", $subarray, PREG_GREP_INVERT)) {
echo 'Yes'; //others found
}
}
TESTID could be a var instead. Man I love some preg_grep!
find = implode(')|(',$mypatternarray);
find.="(".find.")";
foreach($subarray as $subar){
if(preg_match("find",$subar)>0)){
echo "id found";
}
}

Recursively breaking out arrays in PHP

{
"ServiceCurrentlyPlaying": {
"fn": "Slideshow-41958.mp4",
"apps": {
"ServiceCurrentlyPlaying": {
"state": "stopped"
}
}
}
}
How would I break anything named ServiceCurrentlyPlaying out of an array? (from json_decode(file, TRUE)) This is probably an easy question to anyone that knows it, but I've been trying to do something that doesn't involve manually hardcoding each array into another empty array (like with a lot of foreach ($outer as $inner) is what I'm doing but having issues since the amount of nesting varies)
Note: I have to deal with around 41958 files that all have different levels of nesting, different amounts and structure, so ..
Result I'd like would be:
{
"fn": "Slideshow-41958.mp4",
"apps": {
"state": "stopped"
}
}
Thanks, very much appreciated.
Not quite optimized maybe, but this is the idea.
$data = json_decode('{ "ServiceCurrentlyPlaying": { "fn": "Slideshow-41958.mp4", "apps": { "ServiceCurrentlyPlaying": { "state": "stopped" } } } }', true);
$modifiedData = breakArray($data);
function breakArray($arr) {
if(is_array($arr) && sizeof($arr)>0) {
$buffer = array();
foreach($arr as $key=>$value) {
if($key==="ServiceCurrentlyPlaying") {
if(is_array($value)) $buffer = array_merge($buffer, breakArray($value));
} else {
$buffer[$key] = (is_array($value) ? breakArray($value) : $value);
}
}
return $buffer;
} else {
return $arr;
}
}
USE
$array=json_decode($jsondata);
$i=0;
foreach($array as $key=>$arr)
{
$out[$i]['fn']=$arr['fn'];
$out[$i]['apps]=$arr['apps']['ServiceCurrentlyPlaying']
$i++;
}

PHP looping multidimensional array

Not entirely sure how to adequately title this problem, but it entails a need to loop through any array nested within array, of which may also be an element of any other array - and so forth. Initially, I thought it was required for one to tag which arrays haven't yet been looped and which have, to loop through the "base" array completely (albeit it was learned this isn't needed and that PHP somehow does this arbitrarily). The problem seems a little peculiar - the function will find the value nested in the array anywhere if the conditional claus for testing if the value isn't found is omitted, and vice versa. Anyway, the function is as followed:
function loop($arr, $find) {
for($i=0;$i<count($arr);$i++) {
if($arr[$i] == $find) {
print "Found $find";
return true;
} else {
if(is_array($arr[$i])) {
$this->loop($arr[$i], $find);
} else {
print "Couldn't find $find";
return false;
}
}
}
}
Perhaps you should change your code to something like:
var $found = false;
function loop($arr, $find) {
foreach($arr as $k=>$v){
if($find==$v){
$this->found = true;
}elseif(is_array($v)){
$this->loop($v, $find);
}
}
return $this->found;
}
This has been working for me for a while.
function array_search_key( $needle_key, $array ) {
foreach($array AS $key=>$value){
if($key == $needle_key) return $value;
if(is_array($value)){
if( ($result = array_search_key($needle_key,$value)) !== false)
return $result;
}
}
return false;
}
OK, what about a slight modification:
function loop($arr, $find) {
for($i=0;$i<count($arr);$i++) {
if(is_array($arr[$i])) {
$this->loop($arr[$i], $find);
} else {
if($arr[$i] == $find) {
print "Found $find";
return true;
}
}
}
return false;
}
Hmm?
Try this: PHP foreach loop through multidimensional array

Categories