How to search recursively by key of a multi-dim array? - php

I am trying to create a recursive function that takes an array and looks for a property name children, and constructs an array out of the matching one.
This is not straight forward because I don't know which block of my JSON data will contain the key children, so I decided to write a recursive function.
I've tried
$testDataJson = '
{
"macAddress": "10:20:30:40:50:81",
"type": "HGW",
"children": [{
"macAddress": "98:D6:D6:D8:FF:34",
"pendingMethods": false,
"lastSeen": "2017-05-24T10:36:35",
"lastSeenLight": "GREEN",
"model": "AP7465CE-TN",
"type": "WIRELESS_ACCESS_POINT"
}, {
"macAddress": "44:66:E9:A1:2C:DC",
"pendingMethods": false,
"lastSeen": "2017-05-24T10:39:01",
"lastSeenLight": "GREEN",
"model": "PLC 200+ DIV -TN",
"type": "POWERLINE"
}, {
"macAddress": "D8:C2:A9:1C:44:47",
"pendingMethods": "False",
"lastSeen": "2017-05-24T10:39:01",
"lastSeenLight": "GREEN",
"model": "PG9073",
"type": "POWERLINE",
"children": [{
"macAddress": "22:CD:E6:8F:8C:B8",
"pendingMethods": false,
"lastSeen": "2017-05-24T10:38:16",
"lastSeenLight": "GREEN",
"model": "PG9073",
"type": "POWERLINE"
}, {
"macAddress": "13:E4:AB:33:36:AC",
"pendingMethods": false,
"lastSeen": "2017-05-24T10:29:13",
"lastSeenLight": "GREEN",
"model": "PG9072",
"type": "POWERLINE_WIRELESS_ACCESS_POINT"
}]
}]
}';
$testDataArray = json_decode($testDataJson,true);
function recursiveKeyFinder($array) {
$result = [];
if (!isset($array['children']) AND is_array($array)) {
return $result;
}else {
foreach($array['children'] as $child){
$result['macAddress'] = $child['macAddress'];
}
return recursiveKeyFinder($array);
}
}
var_dump(recursiveKeyFinder($testDataArray));
Result: Nothing from var_dump().
Desired result:
["macAddress": "98:D6:D6:D8:FF:34",
"macAddress": "44:66:E9:A1:2C:DC",
"macAddress": "D8:C2:A9:1C:44:47",
"macAddress": "22:CD:E6:8F:8C:B8",
"macAddress": "13:E4:AB:33:36:AC"]
How can I investigate this?

PHP already offers a specific tool for this operation: array_walk_recursive(); so there is no need to reinvent the wheel.
Your task can be swiftly, concisely completed with just one function call after preparing your json data.
Omitting the first macAddress (unwanted) value is done by passing only the subarray with the key of 'children' to array_walk_recursive().
Code: (Demo)
$array=json_decode($testDataJson,true)['children']; // this avoids including the first "non-children" macAddress value.
array_walk_recursive($array,function($v,$k)use(&$result){if($k==='macAddress') $result[]=$v;});
var_export($result);
Result:
array (
0 => '98:D6:D6:D8:FF:34',
1 => '44:66:E9:A1:2C:DC',
2 => 'D8:C2:A9:1C:44:47',
3 => '22:CD:E6:8F:8C:B8',
4 => '13:E4:AB:33:36:AC',
)
Alternatively, the input array can be prepared like this:
$array=array_diff_key(json_decode($testDataJson,true),['macAddress'=>'']);
This will ensure you don't accidentally grab any non-children macAddress values.

Like Barmar siad "You have infinite recursion."
This is my solution. It prints out all mac address
function recursiveKeyFinder($array) {
$result = [];
$result[] = $array['macAddress'];
if (isset($array['children'])) {
foreach($array['children'] as $child){
$result = array_merge($result,recursiveKeyFinder($child));
}
}
return $result;
}
Here the result
array (size=6)
0 => string '10:20:30:40:50:81' (length=17)
1 => string '98:D6:D6:D8:FF:34' (length=17)
2 => string '44:66:E9:A1:2C:DC' (length=17)
3 => string 'D8:C2:A9:1C:44:47' (length=17)
4 => string '22:CD:E6:8F:8C:B8' (length=17)
5 => string '13:E4:AB:33:36:AC' (length=17)
Hope this can help

Related

modify json data in php laravel

I've a sample json data
{
"cities": {
"total": 100,
"count":5000,
"cities_list" : [{
"name": "city1",
"count": 1000
},
{
"name": "city2",
"count": 2000
}
]
}
}
How can I append the cities_list array directly to cities which would look like
{
"cities": [{
"name": "city1",
"count": 1000
},
{
"name": "city2",
"count": 2000
}
]
}
Not sure, about all the stuff, but i assume your "json" or just associative array is in variable (for example foo)
Now you should set:
$foo["cities"] = $foo["citites"]["citites_list"];
Let's say that your JSON result is stored in a variable called $results
$results = json_decode($results,true); //convert json to array
$modifiedArray= [];
$modifiedArray= $results['cities']['cities_list']->map(function ($item, $key) {
return ['cities'][][
'name' => $item->name,
'count' => $item->count
];
});
This is more of a laravel approach since you also tagged laravel. Your modified array is an array type if you want to be a JSON again you have to encode it into a JSON.
You can use the Unset function
$data= json_decode($your_json);
$json_arr = json_decode($your_json, true);
unset($data->total);
unset($json_arr['total']);

PHP - Slice nested array

I have two nested arrays with different length. I want to make length of second array as per first array, see below examples to get idea. Just remove all those items which don't exist in first array. Sometime second array has more values then first array, in this case my tree structure breaks.
These arrays are nested array so simple array_slice not working.
Here are the structure of array.
First Array
"1": {
"id": "1",
"username": "username",
"children": [
{
"id": "-1",
"username": "NULL",
"children": [
{
"id": "-1",
"username": "NULL",
"children": [
{
"id": "-1",
"username": "NULL",
"children": []
}
]
}
]
}
]
}
Second Array
"157": {
"id": "157",
"username": "test1",
"children": [
{
"id": "158",
"username": "test1",
"children": [
{
"id": "159",
"username": "test2",
"children": [
{
"id": "160",
"username": "test3",
"children": []
},
{
"id": "160",
"username": "KICK ME BECAUSE I M NOT EXIST IN FIRST ARRAY",
"children": []
}
]
}
]
},
{
"id": "160",
"username": "KICK ME BECAUSE I M NOT EXIST IN FIRST ARRAY",
"children": [
{
"id": "159",,
"username": "KICK ME BECAUSE I M NOT EXIST IN FIRST ARRAY",
"children": [
{
"id": "161",
"username": "KICK ME BECAUSE I M NOT EXIST IN FIRST ARRAY",
"children": []
}
]
}
]
}
]
}
Expected Output
"157": {
"id": "157",
"username": "test1",
"children": [
{
"id": "158",
"username": "test1",
"children": [
{
"id": "159",
"username": "test2",
"children": [
{
"id": "160",
"username": "test3",
"children": []
},
]
}
]
},
]
}
I am trying this method, but it is not working.
$firstCount = (array_map('count', $actualTree));
$secondCount = (array_map('count', $emptyTree));
$chunk = array_slice($actualTree, 0 , $second[$this->userId], true);
Use Case
The thing which I want to do is that remove those array childrens completely which are not exists in first array. I am building a binary tree upto three levels. First array already has a binary tree with empty values. The second array is data that is coming from the database, and I am simply replacing empty data with the actual data using array_replace. This is working good until second array has more values then first array. So to make it working I have to remove those extra elements.
Could anyone please help me to make there length same. Any help will be appreciated.
Thanks in advance.
A Stack Overflow miracle has occurred... I got a recursive snippet to work on the first pass! Usually it takes me a good hour or two to write something that works.
I don't know if I can make it any tighter/better OR if it will fail on any fringe cases, but:
it works for your sample input
it is midnight for me, I'm tired, and I have to work in the morning
Effectively, it synchronously & recursively iterates each array and stores each level of the entry array to the output array so long as the same level keys exists in the structure array.
Code: (Demo)
function truncateRecursive($structure, $entry) {
$output = [];
while (($structureKey = key($structure)) !== null && ($entryKey = key($entry)) !== null) {
$output[$entryKey] = !is_array($entry[$entryKey])
? $entry[$entryKey]
: truncateRecursive($structure[$structureKey], $entry[$entryKey]);
unset($structure[$structureKey], $entry[$entryKey]);
}
return $output;
}
var_export(truncateRecursive($structure, $entry));
Output:
array (
157 =>
array (
'id' => '157',
'username' => 'test1',
'children' =>
array (
0 =>
array (
'id' => '158',
'username' => 'test1',
'children' =>
array (
0 =>
array (
'id' => '159',
'username' => 'test2',
'children' =>
array (
0 =>
array (
'id' => '160',
'username' => 'test3',
'children' =>
array (
),
),
),
),
),
),
),
),
)

Creating multi dimensional arrays, and handeling same key name issues for json_encode

I am trying to adhere to an integration requirement of having multiple items with the same key names, including its meta data into a main array to properly json_encode.
I have tried splitting out and joining arrays, array_push. The only workable solution I have is to manually build this part of the json package. Any help would be greatly appreciated.
Here is a sample of what I am struggling with:
$message_pack["Header"]["Sequence"] = 'TEST1';
$message_pack["Header"]["TC"] = "1";
$message_pack["ItemDetail"]["ItemName"] = "Item1";
$message_pack["ItemDetail"]["ItemCode"] = "123";
$message_pack["ItemDetail"]["Itemname"] = "Item2";
$message_pack["ItemDetail"]["ItemCode"] = "234";
$json_msg = json_encode($message_pack);
This will obviously only take the last value passed to the matching key name.
I need to adhere to this json format:
{
"Header": {
"Sequence": "TEST1",
"TC": "1",
},
"ItemDetail": [{
"ItemName": "Item1",
"ItemCode": "123" }
{ "ItemName": "Item2",
"ItemCode": "234" }]
}
You need to make "ItemDetail" an array, else you'll overwrite $message_pack["ItemDetail"]["Itemname"] and $message_pack["ItemDetail"]["ItemCode"]:
<?php
$message_pack["Header"]["Sequence"] = 'TEST1';
$message_pack["Header"]["TC"] = "1";
$message_pack["ItemDetail"][] = ["ItemName" => "Item1", 'ItemCode' => 123];
$message_pack["ItemDetail"][] = ["ItemName" => "Item2", 'ItemCode' => 234];
$json_msg = json_encode($message_pack, JSON_PRETTY_PRINT);
echo ($json_msg);
will output:
{
"Header": {
"Sequence": "TEST1",
"TC": "1"
},
"ItemDetail": [
{
"ItemName": "Item1",
"ItemCode": 123
},
{
"ItemName": "Item2",
"ItemCode": 234
}
]
}

How to format the JSON output to the desired format

I am trying to create a custom controller for the WordPress JSON API plugin and so far everything is working except the JSON Data I have is not in the correct format.
This is my current JSON output:
{
"status": "ok",
"all_tags": {
"tag-1": {
"term_name": "Tag 1",
"category_details": {
"0": {
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
},
"2": {
"category_ID": 13,
"category_name": "category 2",
"category_count": 1
}
}
},
"tag-2": {
"term_name": "Tag 2",
"category_details": [
{
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
}
]
}
}
}
However, in order to parse the data I must have the json data in a specific format. The correct format should be like this:
{
"status": "ok",
"all_tags": [
{
"id": 1,
"term_name": "Tag 1",
"category_details": [
{
"id": 2,
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
},
{
"id": 3,
"category_ID": 13,
"category_name": "category 2",
"category_count": 1
}
]
},
{
"id": 2,
"term_name": "Tag 2",
"category_details": [
{
"id": 2,
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
}
]
}
]
}
This is how I am creating the array for the json:
<?php
...
$cats_all = array(); // the array
if (!isset($cats_all[$custom_term->slug])) {
// create the array
$cats_all[$custom_term->slug] = array(
'term_name' => $custom_term->name,
'category_details' => array(
array(
'category_ID' => $categories[0]->term_id,
'category_name' => $categories[0]->name,
'category_count' => $mycats[0]->category_count
)
)
);
} else {
$cats_all[$custom_term->slug]['category_details'][] = array(
'category_ID' => $categories[0]->term_id,
'category_name' => $categories[0]->name,
'category_count' => $mycats[0]->category_count
);
}
...
// remove duplicates
$input = $this->super_unique( $cats_all );
// return the array for json output by the plugin
return array(
'all_tags' => $input,
);
Any help will be greatly appreciated. Also the entire controller can be viewed here.
There are two things you need to achieve:
The all_tags value must be a sequential array, not an associative one. This you can achieve by taking the array_values at the last statement:
return array(
'all_tags' => array_values($input)
);
The category_details values must be sequential arrays, not associative ones. This one is more tricky, as you actually do create them as sequential arrays, but the function super_unique will sometimes turn them into associative arrays, when it eliminates at least one duplicate. I suggest this fix to the function super_unique by adding two statements, around this one:
$result = array_map( 'unserialize', array_unique( array_map( 'serialize', $array ) ) );
To get this:
$is_seq = end(array_keys($array)) == count($array)-1;
$result = array_map( 'unserialize', array_unique( array_map( 'serialize', $array ) ) );
if ($is_seq) $result = array_values($result);
If you get an error on end, then you can use this for that line instead:
end($array); $is_seq = key($array) == count($array)-1;
The $is_seq variable checks that $array is sequential, and if so, calls array_values after the removal of the duplicates, which always returns a sequential array.
I think that the problem here is this:
"0": {
"category_ID": 8,
"category_name": "category 1",
"category_count": 2
},
"2": {
"category_ID": 13,
"category_name": "category 2",
"category_count": 1
}
if you want an array to be a json encoded array, indexes must be numeric and correlatives (0,1,2,3...)
Probably, after using your $this->super_unique( $cats_all ), or inside this function, you should call array_values on every array that has been reindexed; it reset the values of the array to 0, 1, ... etc... and when encoded, it will be an array isntead of an object.
Generally speaking, it´s a good practice to use $array = array_values($array) after using array_filter(...) to correctly reindex the array, elsewhere, you can get indexes like 0,2,7... etc...
Let me know if you need more details

Getting key/value from JSON

We are working with an API that brings back JSON in this format:
[
{
"Id": "d7526186-361c-e611-80da-00155df41a0a",
"LogicalName": "contact",
"Attributes": [
{
"Key": "customertypecode",
"Value": {
"Value": 1
}
},
{
"Key": "merged",
"Value": false
},
{
"Key": "territorycode",
"Value": {
"Value": 1
}
}
],
"EntityState": null,
"FormattedValues": [
{
"Key": "customertypecode",
"Value": "Default Value"
},
{
"Key": "address2_addresstypecode",
"Value": "Default Value"
},
{
"Key": "merged",
"Value": "No"
},
{
I am currently using foreach to organise this into a new, cleaner array - but the code base is getting rather large.
What would be the cleanest way of getting specific values based on specifying a key name?
Thanks so much for your help.
First using json_decode to convert to an array, then you can access it in any way you would a normal multi dimensional array... e.g.
$array = array(
"foo" => "bar",
42 => 24,
"multi" => array(
"dimensional" => array(
"array" => "foo"
)
)
);
var_dump($array["foo"]);
var_dump($array[42]);
var_dump($array["multi"]["dimensional"]["array"]);
from http://php.net/manual/en/language.types.array.php

Categories