How to format the JSON output to the desired format - php

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

Related

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
}
]
}

PHP: Convert Object to Associative Array with Object Property as Key

In PHP, I'd like to convert an array of objects like the following to a PHP array, using one of the properties as the associative array keys.
[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]
like this...
[
2 => "Suzy",
3 => "Joe",
4 => "Sara"
]
I can't use array_map because you can't set the keys from my understanding, but I'm wondering if there's a one-liner way to do it without a foreach loop.
To be clear, I want to maintain the keys in the output array, not puts the original keys inside the new array values like they do here: PHP's array_map including keys
It appears by "object" you mean a JSON object. Given that, you can use array_column() to pull out a single column from each row, and then array_combine() to use one column for the keys and another for the values:
$json = '[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]';
$array = json_decode($json, true);
$out = array_combine(array_column($array, 'id'), array_column($array, 'name'));
print_r($out);
Yields:
Array
(
[2] => Suzy
[3] => Joe
[4] => Sara
)
2 liners and has a foreach though.
<?php
$json = '[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]';
$new_array = [];
foreach(json_decode($json,true) as $each_object) $new_array[$each_object['id']] = $each_object['name'];
print_r($new_array);
$json = '[
{ "id": 2, "name": "Suzy" },
{ "id": 3, "name": "Joe" },
{ "id": 4, "name": "Sara" }
]';
$array = json_decode($json, true);
$result = array_column($array, 'name', 'id');

Best Practice To Convert Regular Array To Associative Using An Attribute as a Key?

Let's say I have the following array
[
{
"id": "16",
"name": "dog",
},
{
"id": "17",
"name": "cat",
},
{
"id": "18",
"name": "mouse",
}
]
I want to use a specific attribute, id as the key for the array. I could do this:
$someArray = [
["id" => "16", "name" => "dog"],
["id" => "17", "name" => "cat"],
["id" => "18", "name" => "mouse"]
];
$newArray = [];
foreach ($someArray as $currItem)
{
$newArray[$currItem["id"]] = $currItem;
}
Then I would have this (the desired outcome)
{
"16": {
"id": "16",
"name": "dog"
},
"17": {
"id": "17",
"name": "cat"
},
"18": {
"id": "18",
"name": "mouse"
}
}
My question is: is there a better way to do this? Do I really have to loop through every item just to redefine my array ever so slightly?
I seem to have found a solution using information from Rizier123's comment and this thread: PHP Change Array Keys
As far as I can tell array_column is only going to give me an array of ids, so I need to use it with array_combine and array_values. Please don't be afraid to post if you have a better answer
$someArray = [
["id" => "16", "name" => "a"],
["id" => "17", "name" => "b"],
["id" => "18", "name" => "c"]
];
$newArray = array_combine(array_column($someArray, "id"), $someArray);
You beat me to the answer but I might contribute a little anyway...
I'm not sure where your original array is coming from, but if you are decoding JSON, then you can provide a second param to force objects to be converted to associative arrays
$contents = trim(file_get_contents("/home/jaith/foo/foo.json"));
$arr = json_decode($contents, TRUE); // note the second parameter
$v = array_combine(array_column($arr, "id"), $arr);
var_dump($v);
EDIT:
If you can tolerate your output array having objects, this might also work:
$contents = trim(file_get_contents("/home/jaith/foo/foo.json"));
$arr = json_decode($contents);
$v = array_combine(
array_column(
array_map(
function($item) { return (array)$item; },
$arr
),
"id"
),
$arr
);
var_dump($v);
Keep in mind though that performance could become a concern for very very large arrays. This is doing a lot of array munging.

Rewriting a JSON string - grouping keys by value into a new two level deep array?

Just building on top of my previous question here:
Rewriting a JSON string - grouping keys by value into a new array?
I am wondering how do I traverse the array given and resort it so that the output comes out something like this:
[{
"Name": "Title 1",
"Date": "2012-12-05",
"rows": [
{
"Subtitle": "Subtitle 1",
"Count1": 566,
"Count2": 105
},
{
"Subtitle": "Subtitle 2",
"Count1": 76,
"Count2": 15
}
]
},
{
"Name": "Title 2",
"Date": "2012-12-06",
"rows": [
{
"Count1": 66,
"Count2": 5
}...
where the original json array is grouped by "Name" column, and then sorted into multi-level structure like this (parent/multiple child json structure for properly building menu in our application)?
The original json data looks like this:
[{
"Name": "Title 1",
"Subtitle": "Subtitle 1",
"Count1": 556,
"Count2": 5,
"Date": "2012-12-05"
}, {
"Name": "Title 1",
"Subtitle": "Subtitle 2",
"Count1": 10,
"Count2": 100,
"Date": "2012-12-05"
}, {
"Name": "Title 3",
"Subtitle": "Subtitle 3",
"Count1": 798,
"Count2": 11,
"Date": "2012-12-04"
}...
and obviously I am looking for a solution in PHP, as I will read in the json data, process it and spit it out reformatted.
Assuming associative arrays and sorting output by name:
$output = array();
for ($data as $row) {
if (!isset($output[$row['Name']])) {
$output[$row['Name']] = array(
'Name' => $row['Name'],
'Date' => $row['Date'],
'rows' => array()
);
}
$output[$row['Name']]['rows'][] = array(
'Subtitle' => $row['Subtitle'],
'Count 1' => $row['Count 1'],
'Count 2' => $row['Count 2']
);
}
hash = {};
result = [];
for (var i = 0; i < data.length; i++) {
var name = data[i].Name;
if (!hash.hasOwnProperty(name)) {
var newitem = { Name: name,
Date: data[i].Date,
rows: []
};
result.push(newitem);
hash[name] = newitem;
}
hash[name].rows.push({ Subtitle: data[i].Subtitle,
Count1: data[i].Count1,
Count2: data[i].Count2
});
}

Categories