I have tried to handle JSON responses with a complex structure and I don't know how to manage it after multiple attempts.
The JSON response must be like this:
"note": {
"vote": 3,
"items":[
{
"value": 1,
"touchArea": {
"x": 122,
"y": 173,
"w": 89,
"h": 89
}
},
{
"value": 2,
"touchArea": {
"x": 122,
"y": 283,
"w": 89,
"h": 89
}
},
{
"value": 3,
"touchArea": {
"x": 122,
"y": 390,
"w": 89,
"h": 89
}
}
]
Note: 'vote' is the max value of the array
As source, i request MYSQL and i get this array ($touch) :
Array ( [0] => V:1,X:122,Y:173,W:89,H:89 [1] => V:2,X:122,Y:283,W:89,H:89 [2] => V:3,X:122,Y:390,W:89,H:89 )
My question is : How to generate this JSON response from PHP with loops, in this example we have only 3 values, but it could be more.
You can use the following to edit and re-save your data from and to json file or string.
<?php
$sInFile = 'in2.json';
$sOutFile = 'out2.json';
$aDevelopers = array();
$aArtists = array();
$aCooks = array();
$sRaw = file_get_contents( $sInFile );
var_dump( $sRaw );
// object
$oData = json_decode( $sRaw );
//array
$aData = json_decode( $sRaw, 1 );
$aNote = $aData[ 'note' ];
$aItems = $aNote[ 'items' ];
var_dump( $aData );
$iCountData = count( $aItems );
// Loops through items and set new values.
for( $i = 0; $i < $iCountData; ++$i )
{
$aItem = $aItems[ $i ];
$aItem[ 'value' ] = 'newvalue';
$aItem[ 'touchArea' ][ 'x' ] = 3455;
$aItem[ 'touchArea' ][ 'y' ] = 43;
$aItems[ $i ] = $aItem;
}
$aNote[ 'items' ] = $aItems;
$aData[ 'note' ] = $aNote;
var_dump( $aData );
$sNewJson = json_encode( $aData );
file_put_contents( $sOutFile, $sNewJson );
?>
Assuming that your result set from MySQL is an array of strings, as implied in your question, you'll need to iterate over this result set, split the string on , and then on :, before massaging the data into the structure you want, before finally converting to json.
The following should work for you given the information you have provided:
<?php
// your initial result set from mysql - an array of strings
$touch = [
'V:1,X:122,Y:173,W:89,H:89',
'V:2,X:122,Y:283,W:89,H:89',
'V:3,X:122,Y:390,W:89,H:89',
];
// map over each element in your result set...
$items = array_map(function($item) {
$array = [];
// first, break each string on `,` to
// get an array of elements; e.g:
// ['V:1', 'X:122', 'Y:173', 'W:89', 'H:89'] etc.
$itemElements = explode(',', $item);
// then, iterate over these elements...
foreach ($itemElements as $itemElement) {
// explode on `:` and assign as a key value pair
list($key, $value) = explode(':', $itemElement);
// then, check the key and add the
// value to the array as appropriate
if ('V' === $key) {
$array['value'] = $value;
} else {
$array['touchArea'][strtolower($key)] = $value;
}
}
return $array;
}, $touch);
// then, build the `$note` array...
$note = [
'note' => [
'vote' => count($items),
'items' => $items,
]
];
// finally, json encode
echo json_encode($note, JSON_PRETTY_PRINT);
This yields:
{
"note": {
"vote": 3,
"items": [
{
"value": "1",
"touchArea": {
"x": "122",
"y": "173",
"w": "89",
"h": "89"
}
},
{
"value": "2",
"touchArea": {
"x": "122",
"y": "283",
"w": "89",
"h": "89"
}
},
{
"value": "3",
"touchArea": {
"x": "122",
"y": "390",
"w": "89",
"h": "89"
}
}
]
}
}
Hope this helps :)
Solution with array_map, explode, array_column and max functions:
$touch = ["V:1,X:122,Y:173,W:89,H:89", "V:2,X:122,Y:283,W:89,H:89", "V:3,X:122,Y:390,W:89,H:89"];
$result_arr = ["note" => ["vote" => 0, "items" => []]]; // basic structure
array_map(function($v) use (&$result_arr){
$arr = explode(",", $v);
$result = [];
foreach ($arr as $value) {
$new_arr = explode(":", $value);
$key = strtolower($new_arr[0]);
if ($key == "v"){
$result["value"] = $new_arr[1];
} else {
$result["touchArea"][$key] = $new_arr[1];
}
}
$result_arr["note"]["items"][] = $result;
}, $touch);
// getting max vote
$result_arr["note"]["vote"] = max(array_column($result_arr["note"]["items"], "value"));
$final_json = json_encode($result_arr, JSON_PRETTY_PRINT);
Related
Okay so not 100% sure how to even ask this correctly but I have an array that I'm trying to consolidate and group together based on each hour, and then data for each year underneath that time.
My current array looks like this:
[
{
"name": "7:00 AM",
"data": [
{
"x": "2019",
"y": 1
}
]
},
{
"name": "7:00 AM",
"data": [
{
"x": "2020",
"y": 221
}
]
}
]
So I'm looking to combine these into a single array based on the name value and then all of the data will be combined:
[
{
"name": "7:00 AM",
"data": [
{
"x": "2019",
"y": 1
},
{
"x": "2020",
"y": 221
}
]
}
]
EDIT: And just to add how I'm originally getting and formatting these values, I'm using Larvel and running the following query:
$shipments = Orders::whereNotNull('shipped_at')->groupBy(DB::raw('YEAR(created_at)'))->selectRaw('count(*) as count, HOUR(shipped_at) as hour')->selectRaw("DATE_FORMAT(created_at, '%Y') label")->orderBy('label')->orderBy('hour')->groupBy('hour')->get();
And that gives me the following data:
And then I'm building the original array like this:
$shipping_chart_year = [];
foreach ($shipments as $item) {
$hour = Carbon::parse($item['hour'] . ':00:00')->timezone('America/Chicago')->format('g:i A');
$shipping_chart_year[] = ['name' => $hour, 'data' => [['x' => $item['label'], 'y' => $item['count']]]];
}
This functions formats array as you expected to have.
function formatArray($array){
$response = [];
$keysIndexes = [];
foreach($array as $value) {
if(!array_key_exists($value['name'], $keysIndexes)) {
array_push($response, $value);
$keysIndexes[$value['name']] = sizeof($keysIndexes);
} else {
array_push($response[$keysIndexes[$value['name']]]['data'], $value['data'][0]);
}
}
return $response;
}
if you want to achieve this with the Laravel Collections (you can do so much with it!), here you go (based on your current array structure).
It is much more readable and way easier to achieve.
$output = collect(json_decode($data, true))
->groupBy('name')
->transform(function ($items, $name) {
// Merge nested arrays into one
$data = $items->map(function ($item) {
return $item['data'][0];
})->toArray();
return [
'name' => $name,
'data' => $data,
];
})
->values() // removes the key
->toJson(); // returns the collection to json
Here is my JSON
[
{
"TIMESTAMP": "2021-06-09 13:13:26",
"COL1": "10",
"COL2": "20",
"COL3": "30"
},
{
"TIMESTAMP": "2021-06-22 13:13:26",
"COL1": "20",
"COL2": "30",
"COL3": "40"
},
{
"TIMESTAMP": "2021-06-21 13:13:26",
"COL1": "1",
"COL2": "2",
"COL3": "3"
},
{
"TIMESTAMP": "2021-06-20 13:13:26",
"COL1": "40",
"COL2": "50",
"COL3": "60"
}
]
I need to refactor the json According to the Column name like (EXPECTED OUTPUT)
[
{
"TITLE":"COL1"
"DATA":[10,20,1,40]
},
{
"TITLE":"COL2"
"DATA":[20,30,2,50]
},
{
"TITLE":"COL3"
"DATA":[30,40,3,60]
},
]
I was tried but it not working
$data = json_decode($result, true);
$refactored = array_map(function($item) {
return (object)[
'TIMESTAMP' => $item['TIMESTAMP'],
'DATA' => [ $item['COL1'], $item['COL2'], $item['COL3'] ]
];
}, $data);
dump($refactored);
Someone help me out with this. The column may be 3 or more and it must be dynamic. Thanks in advance.
You can transform your JSON like this:
$data = json_decode($result, true);
$refactored = array_reduce($json, function($carry, $item) {
foreach($item as $key => $value) {
if (str_starts_with($key, 'COL')) {
$index = substr($key, 3, 1) - 1;
if (!isset($carry[$index])) {
$carry[$index] = [
'Title' => $key
];
}
$carry[$index]['Data'][] = $value;
}
}
return $carry;
}, []);
dump($refactored);
I am always a fan of using the value you want to group your data by as a temporary array key, that makes things easier, and can simply be reset by using array_values afterwards.
$input = json_decode('…', true);
$output = [];
foreach($input as $item) {
foreach($item as $key => $value) {
if($key != 'TIMESTAMP') {
$output[$key]['TITLE'] = $key;
$output[$key]['DATA'][] = (int)$value;
}
}
}
$output = array_values($output);
echo json_encode($output);
All you have to do is re-encode the processed data:
json_encode($refactored) will give you the output that you want.
P.S. you don't have to cast to an object. It works as array as well.
I want to merge two same keys in an array and get the sum of the values.
I want the same structure as it is now.Because this data needs to be converted to JSON.
This is what i get now.
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 9,
"user": "Student",
"id": "45"
},
{
"count_of_invites": 4,
"user": "Student",
"id": "45"
}
]
}
As you can see the id 45 are repeated.As i want the result as,
Expected output
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 13,
"user": "Student",
"id": "45"
}
]
}
As you can see the duplicate entry should be removed as well as the count_of_invites of duplicate entry should be added.
<?php
$data = [
[
'id' => 2,
'name' => 'Paul',
'count' => 4
],
[
'id' => 3,
'name' => 'Peter',
'count' => 5
],
[
'id' => 3,
'name' => 'Peter',
'count' => 7
]
];
foreach($data as $array)
$counts[$array['id']][] = $array['count'];
$counts = array_map('array_sum', $counts);
foreach($data as $k => $array)
$data[$k]['count'] = $counts[$array['id']];
$data = array_unique($data, SORT_REGULAR);
print json_encode($data, JSON_PRETTY_PRINT);
Output:
[
{
"id": 2,
"name": "Paul",
"count": 4
},
{
"id": 3,
"name": "Peter",
"count": 12
}
]
You can achieve it this way:
$ids = array();
$output = array();
foreach ($input as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]]=$count($output);
$output[]=$value;
} else {
$output[$ids[$value["id"]]]["count_of_invites"] = $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}
The count method was declared as variable and i've added with addition assignment operator.
Thank You for helping.
$ids = array();
$output = array();
foreach ($response as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]] = count($output);
$output[] = $value;
}
else {
$output[$ids[$value["id"]]]["count_of_invites"] += $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}
How to group array by 'tax'and 'concept' and SUM attribute 'val'?
I looking for create a single object when tax and concept are the same, also SUM the val.
I tried with simple foreach and validation inside, but doesnt work.
thanks.
echo json_encode($array);
Array Print
[
{
"tax": "10",
"concept": "TUC",
"val": "10"
},
{
"tax": "10",
"concept": "TUC",
"val": "86"
},
{
"tax": "15",
"concept": "TUC",
"val": "8"
},
{
"tax": "11",
"concept": "IPS",
"val": "6"
},
{
"tax": "11",
"concept": "IPS",
"val": "45"
}
]
Expected Result
[
{
"tax": "10",
"concept": "TUC",
"val": "96"
},
{
"tax": "15",
"concept": "TUC",
"val": "8"
},
{
"tax": "11",
"concept": "IPS",
"val": "51"
}
]
You can use array_reduce() here. You'll use the tax value as a key you're grouping on, and reduce the array to unique tax elements while simultaneously producing the sum of val values. The only caveat with this approach is that transforming it to JSON will make PHP think that the outer element is an object and not an array (even though it is an array, this is because we end up using non-default array indexing). But, this is easily mitigated with a call to array_values().
$array = // your array from above
$result = array_reduce($array, function($carry, $item) {
if(!isset($carry[$item->tax])) {
$carry[$item->tax] = $item;
} else {
$carry[$item->tax]->val += $item->val;
}
return $carry;
});
$result = array_values($result);
echo json_encode($result);
You can see from this demo that it produces:
[{
"tax": "10",
"concept": "TUC",
"val": 96
}, {
"tax": "15",
"concept": "TUC",
"val": "8"
}, {
"tax": "11",
"concept": "IPS",
"val": 51
}]
$array // Your array
$result = [];
array_walk($array, function($object) use (&$result) {
$notExist = true;
foreach ($result as $item) {
if ($item->tax == $object->tax && $item->concept == $object->concept) {
$item->val += $object->val;
$notExist = false;
break;
}
}
if ($notExist) {
array_push($result, $object);
}
});
echo json_encode($result);
First group the objects:
$groups = [];
foreach($array as $object){
$groups[$object->tax . "\0" . $object->concept] = $object;
// I use the NUL byte to delimit, assuming it is absent in $tax and $concept
}
Then map each group into a summed-up object.
$output = array_map(function(array $group){
$object = new stdClass;
$object->tax = $group[0]->tax;
$object->concept = $group[0]->concept;
$object->val = array_reduce($group, function($carry, $item){
return $carry + $item->val;
}, 0);
return $object;
}, $groups);
<?php
$arr_str = '[
{"tax":"10", "concept":"TUC", "val":"10"},
{"tax":"10", "concept":"TUC", "val":"86"},
{"tax":"15", "concept":"TUC", "val":"8"},
{"tax":"11", "concept":"IPS", "val":"6"},
{"tax":"11", "concept":"IPS", "val":"45"}
]';
$arr = json_decode($arr_str);
$tmp_array = [];
foreach ($arr as $e) {
// combine "tax" and "concept" so we can use both of them as key. Use "_" as delimiter.
$key = $e->tax . "_" . $e->concept;
// sum the "val" if the key exists, otherwise assign it
isset($tmp_array[$key]) ? $tmp_array[$key] += $e->val : $tmp_array[$key] = $e->val;
}
$grouped_array = [];
foreach ($tmp_array as $k => $v) {
// ungroup the key so we can create an array like the original one
$tmp = explode("_", $k);
$grouped_array[] = (object)["tax" => $tmp[0], "concept" => $tmp[1], "val" => $v];
}
echo json_encode($grouped_array);
?>
I'm not able to get this code working:
$paths = ['2014/','2014/04/','2015/'];
$tree = array();
foreach ($paths as $path) {
$dir_exploded = explode("/", $path);
array_pop($dir_exploded);
$tmp = array();
for ($i = count($dir_exploded) - 1; $i >= 0; $i--) {
if ($i == count($dir_exploded) - 1) {
$children = array();
} else {
$children = array($tmp);
}
$tmp = array('text' => $dir_exploded[$i], 'children' => $children);
}
$tree = array_replace_recursive($tree, $tmp);
}
echo(json_encode(array(array('text' => '/', 'children' => array($tree)))));
I get:
[
{
"text": "/",
"children": [
{
"text": "2015",
"children": [
{
"text": "04",
"children": []
}
]
}
]
}
]
So 2014 have been delete by the merge of this 2 arrays. I would like to get:
[
{
"text": "/",
"children": [
{
"text": "2014",
"children": [
{
"text": "04",
"children": []
}
]
},{
"text": "2015",
"children": []
}
]
}
]
At least I want to send this tree by json using json_encode, or a better way if you know one.
Try this code, I have change little code of yours and add some extra paths.
$paths = array('2014/', '2014/04/','2014/03/','2015/');
$new_tree = $temp_new_tree = array();
foreach ($paths as $path)
{
$dir_exploded = explode("/", $path);
array_pop($dir_exploded);
$temp_new_tree = (!empty($new_tree)) ? $new_tree : array();
$tmp = $tree = array();
for ($i = count($dir_exploded) - 1; $i >= 0; $i--)
{
if ($i == count($dir_exploded) - 1) {
$children = array();
} else {
$children = array($tmp);
}
$tmp = array('text' => $dir_exploded[$i], 'children' => $children);
}
$tree = array_replace_recursive($tree, $tmp);
$new_tree[$dir_exploded[0]] = $tree;
if(array_key_exists($dir_exploded[0], $temp_new_tree))
{
$new_tree[$dir_exploded[0]]['children'] = array_merge($new_tree[$dir_exploded[0]]['children'], $temp_new_tree[$dir_exploded[0]]['children']);
}
}
//echo json_encode(array(array('text' => '/', 'children' => array($new_tree))));
$new_tree = array_shift(array_chunk($new_tree, count($new_tree)));
echo json_encode(array(array('text' => '/', 'children' => $new_tree)));
Output will be like this:--
[
{
text: "/",
children: [
{
text: "2014",
children: [
{
text: "03",
children: [ ]
},
{
text: "04",
children: [ ]
}
]
},
{
text: "2015",
children: [ ]
}
]
}
]
Hope this will help you...
You can use following;
<?php
$paths = array('2014/01/02','2014/04/03','2015/07');
$all = array();
foreach ($paths as $path) {
$temp = explode("/", $path);
$all[] = tree($temp, 0);
}
var_dump($all);
function tree($arr, $i) {
if (count($arr) == ($i + 1)) return $arr[$i];
return array(
"text" => $arr[$i],
"children" => tree($arr, $i+1)
);
}
Here is a working demo: codepad