I am creating an e-commerce website where I have to give the categories that a shop has in a particular array . Currently the data getting retrieved from a mysql table contains the same category id's in different array items if the array has different subcategory , I have to gather the same category id's in the same array and for subcategories create a nested array . The code is on laravel 4.2 . Here is the format of data coming right now ,
"data": [
{
"id": 1,
"name": "Fruits",
"sub_category_names": [
"Dairy Whiteners"
],
"total_items": 69
},
{
"id": 1,
"name": "Fruits",
"sub_category_names": [
"Tea & Coffee"
],
"total_items": 69
},
{
"id": 1,
"name": "Fruits",
"sub_category_names": [
"Concentrates - Tang etc"
],
"total_items": 69
},
{
"id": 2,
"name": "Beverages",
"sub_category_names": [
"Tea & Coffee"
],
"total_items": 28
},
{
"id": 2,
"name": "Beverages",
"sub_category_names": [
"Concentrates - Tang etc"
],
"total_items": 28
}
]
Here is what I need ,
"data": [
{
"id": 1,
"name": "Fruits",
"sub_category_names": [
"Dairy Whiteners" , "Concentrates - Tang etc" , "Tea & Coffee"
],
"total_items": 69
} ,
{
"id": 2,
"name": "Beverages",
"sub_category_names": [
"Tea & Coffee" , "Concentrates - Tang etc"
],
"total_items": 28
}
]
The code I wrote for the above ,
// For the current categories create a common array for subcategories for same categories
$filteringSelectedCategories = [];
$subCategoriesNamesLocal = [];
$innerIndex = 0;
for ($i = 0; $i < count($selectedCategories); $i++) {
// to prevent undefined offset error
if (!isset($selectedCategories[$i + 1])) {
continue ;
// if 6 don't exist then deal with 5
}
// if the id of two items is same then push that sub category name in the same array
if ($selectedCategories[$i]['id'] === $selectedCategories[$i + 1]['id']) {
array_push($subCategoriesNamesLocal, $selectedCategories[$i]['sub_category_names']);
}
// if the id is different then push the array values with the sub category name in an array
else {
$filteringSelectedCategories[$innerIndex]['id'] = $selectedCategories[$i]['id'];
$filteringSelectedCategories[$innerIndex]['name'] = $selectedCategories[$i]['name'];
$filteringSelectedCategories[$innerIndex]['sub_category_names'] = $subCategoriesNamesLocal;
$filteringSelectedCategories[$innerIndex]['total_items'] = $selectedCategories[$i]['total_items'];
// nullify the array after assigning the value
$subCategoriesNamesLocal = [];
// increment the new array index
$innerIndex = $innerIndex + 1;
}
}
Here is the output I get from the above ,
"data": [
{
"id": 1,
"name": "Fruits",
"sub_category_names": [
[
"Dairy Whiteners"
],
[
"Tea & Coffee"
]
],
"total_items": 69
}
]
I'm not entirely sure I see what offset error could occur but I believe you could easily get away with;
foreach ($selectedCategories as $key => $data) {
$newKey = $data['id'];
$filteringSelectedCategories[$newKey]['id'] = $data['id'];
$filteringSelectedCategories[$newKey]['name'] = $data['name'];
$filteringSelectedCategories[$newKey]['sub_category_names'][] = $data['sub_category_names'];
$filteringSelectedCategories[$newKey]['total_items'] = $data['total_items'];
}
You can not make an array push directly in $subCategoriesNamesLocal because you are nesting arrays. Make an array outside and then concatenate it to the field.
Try this:
// For the current categories create a common array for subcategories for same categories
$filteringSelectedCategories = [];
$subCategoriesNamesLocal = [];
$innerIndex = 0;
for ($i = 0; $i < count($selectedCategories); $i++) {
// to prevent undefined offset error
if (!isset($selectedCategories[$i + 1])) {
continue ;
// if 6 don't exist then deal with 5
}
// if the id of two items is same then push that sub category name in the same array
if ($selectedCategories[$i]['id'] === $selectedCategories[$i + 1]['id']) {
array_push($subCategoriesNamesLocal, $selectedCategories[$i]['sub_category_names']);
}
// if the id is different then push the array values with the sub category name in an array
else {
$filteringSelectedCategories[$innerIndex]['id'] = $selectedCategories[$i]['id'];
$filteringSelectedCategories[$innerIndex]['name'] = $selectedCategories[$i]['name'];
$filteringSelectedCategories[$innerIndex]['sub_category_names'] = '"' . implode('","', $subCategoriesNamesLocal) . '"';
}
$filteringSelectedCategories[$innerIndex]['total_items'] = $selectedCategories[$i]['total_items'];
// nullify the array after assigning the value
$subCategoriesNamesLocal = [];
// increment the new array index
$innerIndex = $innerIndex + 1;
}
}
Related
I am trying to create a file explorer type treeview JSON to be read by FancyTree for a project I'm attempting.
The files are stored in a database, with an ID, name, URL, Type and code fields. The mock database looks like this:
ID name URL. Type code
1 test dir.dir1 txt sometext
2 next dir.dir1 txt somemoretext
3 main dir txt evenmoretext
I need to build the JSON tree view from this data, using the URL as a path (period being the delimiter) and the files being inside the final directory so the tree looks like
/dir/dir1/test.txt
/dir/dir1/next.txt
/dir/main.txt
FancyTree JSON output should look like
[
{
"title": "dir",
"folder": true,
"children": [
{
"title": "dir1",
"folder": true,
"children": [
{
"title": "test.txt",
"key": 1
}, {
"title": "next.txt",
"key": 2
}
]
}, {
"title": "main.txt",
"key": 3
}
]
}
]
Currently, I'm getting the data from the database into $scriptArray
SELECT 'name','url','type','id' FROM.....
I'm then sorting and building a tree with
$url = array_column($scriptArray, 'url');
array_multisort($url, SORT_ASC, $scriptArray);
$result = [];
foreach($scriptArray as $item) {
$loop = 0;
$keys = array_reverse(explode('.', $item->url));
$tmp = $item->name;
$tmp2 = $item->type;
foreach ($keys as $keyid => $key) {
if($loop == 0) {
$tmp = ["title" => $tmp.".".$tmp2, 'key' => $item->id];
} else {
$tmp = ["title" => $keys[$keyid - 1], "folder" => true, "children" => [$tmp]];
}
$loop++;
}
$tmp = ["title" => $keys[count($keys)-1], "folder" => true, "children" => [$tmp]];
$result[] = $tmp;
}
However, the output I'm getting is.
[
{
"title": "dir",
"folder": true,
"children": [
{
"title": "dir2",
"folder": true,
"children": [
{
"title": "test.txt",
"key": 1
}
]
}
]
},
{
"title": "dir",
"folder": true,
"children": [
{
"title": "dir2",
"folder": true,
"children": [
{
"title": "next.txt",
"key": 2
}
]
}
]
},
{
"title": "main.txt",
"key": 3
}
]
I have tried applying an array_merge, array_merge_recursive and various others without success. Can anyone help with this?
Working with loop won't work unless you know per advance the maximum depth of your folder hierarchy.
A better solution is to build the folder path "recursively", and append the file to the final folder.
This can be achieved with by creating a reference with the & operator, and navigate to its children until the whole path is build :
$result = array();
foreach($files as $file)
{
// build the directory path if needed
$directories = explode('.', $file->url); // get hierarchy of directories
$currentRoot = &$result ; // set the pointer to the root directory per default
foreach($directories as $directory)
{
// check if directory already exists in the hierarchy
$dir = null ;
foreach($currentRoot as $i => $d)
{
if(isset($d['folder']) && $d['folder'] and $d['title'] == $directory)
{
$dir = &$currentRoot[$i] ;
break ;
}
}
// create directory if missing
if(is_null($dir))
{
$item = array(
'title' => $directory,
'folder' => true,
'children' => array()
);
$currentRoot[] = $item ;
$dir = &$currentRoot[count($currentRoot)-1];
}
// move to the next level
$currentRoot = &$dir['children'] ;
unset($dir);
}
// finally append the file in the latest directory
$currentRoot[] = array(
'title' => $file->name . '.' . $file->type,
'key' => $file->id,
);
unset($currentRoot);
}
echo json_encode($result);
This question already has answers here:
How to GROUP BY and SUM PHP Array? [duplicate]
(2 answers)
Closed 9 months ago.
I'm having a hard time manipulating an array of objects in PHP. I need to group the objects by id, while summing up the points.
Starting array of objects:
[
{
"id": "xx",
"points": 25
},
{
"id": "xx",
"points": 40
},
{
"id": "xy",
"points": 40
},
]
What I need:
[
{
"id": "xx",
"points": 65
},
{
"id": "xy",
"points": 40
},
]
As a frontender, I'm having a hard time with object/array manipulations in PHP. Any help would be greatly appreciated!
i hope this answer help you
first i will change objects to array and return the result to array again
$values =[
[
"id"=> "xx",
"points"=> 25
],
[
"id"=> "xx",
"points"=> 40
],
[
"id"=> "xy",
"points"=> 40
],
];
$res = array();
foreach($values as $vals){
if(array_key_exists($vals['id'],$res)){
$res[$vals['id']]['points'] += $vals['points'];
$res[$vals['id']]['id'] = $vals['id'];
}
else{
$res[$vals['id']] = $vals;
}
}
$result = array();
foreach ($res as $item){
$result[] = (object) $item;
}
output enter image description here
Parse JSON as Object
Aggregate Data
Put back as JSON
$json = <<<'_JSON'
[
{
"id": "xx",
"points": 25
},
{
"id": "xx",
"points": 40
},
{
"id": "xy",
"points": 40
}
]
_JSON;
$aggregate = [];
foreach(json_decode($json) as $data) {
if(!isset($aggregate[$data->id])) $aggregate[$data->id] = 0;
$aggregate[$data->id] += $data->points;
}
$output = [];
foreach($aggregate as $id => $points) {
$output[] = ['id' => $id, 'points' => $points];
}
echo json_encode($output);
[{"id":"xx","points":65},{"id":"xy","points":40}]
You may use array_reduce buil-in function to do the job. Also, when looping through the object's array (the callback), you should check if the result array has the current item's ID to verify that wether you need to add the item to the result array or to make the sum of points attributes.
Here's an example:
// a dummy class just to replicate the objects with ID and points attributes
class Dummy
{
public $id;
public $points;
public function __construct($id, $points)
{
$this->id = $id;
$this->points = $points;
}
}
// the array of objects
$arr = [new Dummy('xx', 25), new Dummy('xx', 40), new Dummy('xy', 40)];
// loop through the array
$res = array_reduce($arr, function($carry, $item) {
// holds the index of the object that has the same ID on the resulting array, if it stays NULL means it should add $item to the result array, otherwise calculate the sum of points attributes
$idx = null;
// trying to find the object that has the same id as the current item
foreach($carry as $k => $v)
if($v->id == $item->id) {
$idx = $k;
break;
}
// if nothing found, add $item to the result array, otherwise sum the points attributes
$idx === null ? $carry[] = $item:$carry[$idx]->points += $item->points;
// return the result array for the next iteration
return $carry;
}, []);
This will result in something like this:
array(2) {
[0]=>
object(Dummy)#1 (2) {
["id"]=>
string(2) "xx"
["points"]=>
int(65)
}
[1]=>
object(Dummy)#3 (2) {
["id"]=>
string(2) "xy"
["points"]=>
int(40)
}
}
Hope that helps, feel free to ask for further help.
Let's use a helper variable called $map:
$map = [];
Build your map:
foreach ($input => $item) {
if (!isset($map[$item["id"]])) $map[$item["id"]] = 0;
$map[$item["id"]] += $item["points"];
}
Now let's build the output:
$output = [];
foreach ($map as $key => $value) {
$output[] = (object)["id" => $key, "points" => $value];
}
I have this array of objects i'm getting from query in mysql, i need th
[
{
"id": "11",
"from_userid": "1996",
"contest_id": "29",
"to_userid": "8",
"vote_date": "2020-10-06 01:40:04",
"count_votes": "1"
},
{
"id": "1",
"from_userid": "82",
"contest_id": "29",
"to_userid": "94",
"vote_date": "2020-09-03 07:06:36",
"count_votes": "1"
},
{
"id": "2",
"from_userid": "82",
"contest_id": "29",
"to_userid": "98",
"vote_date": "2020-09-03 07:06:36",
"count_votes": "0"
}
]
I need the object which has highest 'count_votes ' for eg- id-11 and 1 have similar count votes. So the function should return those 2 objects.
The function i am using returns only one object. I need both of the objects whichever maybe but the highest(count_votes) objects.
Expected Output-
[
{
"id": "11",
"from_userid": "1996",
"contest_id": "29",
"to_userid": "8",
"vote_date": "2020-10-06 01:40:04",
"count_votes": "1"
},
{
"id": "1",
"from_userid": "82",
"contest_id": "29",
"to_userid": "94",
"vote_date": "2020-09-03 07:06:36",
"count_votes": "1"
}
]
Function used-
function max_attribute_in_array($array, $prop) {
return max(array_map(function($o) use($prop) {
return $o;
},
$array));
}
And tried this also-
function get_highest($arr) {
$max = $arr[0]; // set the highest object to the first one in the array
foreach($arr as $obj) { // loop through every object in the array
$num = $obj['count_votes']; // get the number from the current object
if($num > $max['count_votes']) { // If the number of the current object is greater than the maxs number:
$max = $obj; // set the max to the current object
}
}
return $max; // Loop is complete, so we have found our max and can return the max object
}
You can use array_column to extract all the count_votes values into an array, which you can then take the max of:
$max = max(array_column($arr, 'count_votes'));
You can then array_filter your array based on the count_votes value being equal to $max:
$out = array_filter($arr, function ($o) use ($max) {
return $o['count_votes'] == $max;
});
Output:
Array
(
[0] => Array
(
[id] => 11
[from_userid] => 1996
[contest_id] => 29
[to_userid] => 8
[vote_date] => 2020-10-06 01:40:04
[count_votes] => 1
)
[1] => Array
(
[id] => 1
[from_userid] => 82
[contest_id] => 29
[to_userid] => 94
[vote_date] => 2020-09-03 07:06:36
[count_votes] => 1
)
)
Demo on 3v4l.org
NOTE max works with single level arrays, so all your objects are converted to int's internally.
As #Nick has pointed out your get_highest can be done via PHP functions:
function get_highest($array, $prop) {
return max(array_column($array, $prop));
}
So all you have to do is filter your array by this get_highest:
$max = get_highest($myArray, 'count_votes');
$maxes = array_filter($myArray, fn($obj) => $obj['count_votes'] === $max);
function get_heighest($arr){
$newArray = array();
$voteCount = 0;
foreach($arr as $obj){
if($obj['count_votes'] >= $voteCount){
array_push($newArray, $obj)
$voteCount = $obj['count_votes'];
}else{
$i = 0;
foreach($newArray as $object){
if($object['count_votes'] < $voteCount){
unset($newArray[$i]);
}
$i++;
}
}
}
return $newArray;
}
I'm creating simple PHP for output data to apexcharts javascript charts. To make apexcharts usable output I need to provide x and y value of the graphs as JSON. below code, I wrote to output the expected JSON.
$data_arr = array();
global $mysqli_conn;
$result = $mysqli_conn->query("sql");
$sql_out = array();
$sql_out = $result->fetch_all(MYSQLI_ASSOC);
$num_rows = mysqli_num_rows($result);
if ($num_rows < 1){
echo "zero";
}else{
foreach($sql_out as $item) {
$data_arr['x'][] = $item['time'];
$data_arr['y'][] = $item['status_code'];
}
}
$test_arr = array(
array(
"name"=>"lock",
"data"=>array($data_arr),
)
);
echo json_encode($test_arr);
my expected json output is like below
[
{
"name": "lock",
"data": [
{
"x": "2019-05-30 07:53:07",
"y": "1470"
},
{
"x": "2019-05-29 07:52:27",
"y": "1932"
}
]
}
]
But when I request data from my code what I'm getting something like this
[
{
"name": "lock",
"data": [
{
"x": [
"2019-05-30 07:53:07",
"2019-05-29 07:52:27",
"2019-05-26 15:46:56",
"2019-05-25 07:39:24"
],
"y": [
"1470",
"1932",
"1940",
"1470"
]
}
]
}
]
How can I create my expected JSON result from PHP code?.
You're creating them on a seprate space. When you declare and push them, put them on one container:
foreach ($sql_out as $item) {
$data_arr[] = array(
'x' => $item['time'],
'y' => $item['status_code']
);
}
When you do this:
$data_arr['x'][] = $item['time'];
$data_arr['y'][] = $item['status_code'];
They are on a separate containers, x and y containers, therefore you get the wrong format, like the one you showed.
When you declare them as:
$data_arr[] = array(
'x' => $item['time'],
'y' => $item['status_code']
);
You're basically tellin to push the whole sub batches but together.
I parse Excel sheet and get this JSON:
[
{
"A":"Samsung",
"Groupe":{
"F":"TV",
"D":"HDR"
}
},
{
"A":null,
"Groupe":{
"F":null,
"D":null
}
},
{
"A":"Sony",
"Groupe":{
"F":"T.V",
"D":"LCD"
}
},
{
"A":"Sony",
"Groupe":{
"F":"PS4",
"D":"Pro edition"
}
},
{
"A":"Sony",
"Groupe":{
"F":"Smart Phone",
"D":"Quad core"
}
}
]
Php code:
$data = [];
for ($row = 15; $row <= 25; $row++) {
$data[] = [
'A' => $worksheet->getCell('A'.$row)->getValue(),
'Groupe' => [
'F' => $worksheet->getCell('F'.$row)->getValue(),
'D' => $worksheet->getCell('D'.$row)->getValue()
]
];
}
How can I organize(sort) json depending on "A"?
I tried this but I still couldn't merge "Groupe" for same "A" together:
Take away NULL colomns.
Create a copy of the Array.
Regroup fields for same element in the new Array(this didnt work)
Code:
$data1 = [];
for ($l = 0; $l < count($data); $l++){
$data1[$l] = $data[$l];
}
for ($j = 0; $j < count($data); $j++) {
if($data[$j]['A'] != NULL){
if($data[$j]['A'] !== $data[$j+1]['A']){
$data1[$j] = $data[$j];
}
else{
$data1[$j]['A']= $data[$j]['A'];
$data1[$j]['Groupe']= array_merge($data[$j]['Groupe'], $data[$j+1]['Groupe']);
}
}
}
EDIT:
The result that I'm getting for $data1 is exactly the same as the input JSON(except that NULL was deleted), so it looks like merge Array didnt work and what I need is:
[
{
"A":"Samsung",
"Groupe":{
"F":"TV",
"D":"HDR"
}
},
{
"A":"Sony",
"Groupe": [{
"F":"T.V",
"D":"LCD"
},{
"F":"PS4",
"D":"Pro edition"
}, {"F":"Smart Phone",
"D":"Quad core"
}]
}]
Plus it's showing me this :
Notice: Undefined offset: 11 in C:\xampp\htdocs\phptoexcel.php on line
43
Line 43: if($data[$j]['A'] !== $data[$j+1]['A']){
Use the A value as key in $data, so you can group by it:
$data = [];
for ($row = 15; $row <= 25; $row++) {
//get A value, skip if A = NULL
$a = $worksheet->getCell('A'.$row)->getValue(),
if($a===NULL)continue;
//get F and D VALUE, skip if one of them = NULL
$f = $worksheet->getCell('F'.$row)->getValue();
$d = $worksheet->getCell('D'.$row)->getValue();
if($f===null || $d===null)continue;
//test if A is a key in $data. If not, create
if(!array_key_exist( $a, $data ){
$data[$a]=[
'A'=>$a,
'Groupe'=>[]
];
}
//Put F and D in a new array in Groupe
$data[$a]['Groupe'][]=["F"=>$f,"D"=>$d];
}
You will end up with:
$data=>
[ "Samsung" =>[ "A" => "Samsung",
"Groupe" => [ 0 =>[ "F" => "TV",
"D" => "HDR"
]
]
],
"Sony" => [ "A" => "Sony",
"Groupe" => [ 0 =>[ "F":"TV",
"D":"HDR"
],
1 =>[ "F":"T.V",
"D":"LCD"
],
2 =>[ "F":"PS4",
"D":"Pro edition"
],
3 =>[ "F":"Smart Phone",
"D":"Quad core"
],
]
]
Try This
$arrUnique = array();
$result = array();
$i=0;
foreach($data as $value){
if($value['A']!=null){
$data1 = [];
$intID = $value['A'];
if( in_array( $intID, $arrUnique ) ) {
$key = array_search ($intID, $arrUnique);
$result[$key]['Groupe'][] = $value['Groupe'];
}else{
$data1['A'] = $value['A'];
$data1['Groupe'][] = $value['Groupe'];
$result[$i]=$data1;
$arrUnique[]=$value['A'];
$i++;
}
}
}
I usually don't perform JSON to JSON transformation using PHP but using jq command line utility.
Given your input JSON file, you can use this jq filter:
jq '[[sort_by(.A)|.[]|select(.A!=null)]|group_by(.A)|.[]as $i|{A:$i[].A,Groupe:$i|map(.Groupe)}]|unique' file
[
{
"A": "Samsung",
"Groupe": [
{
"F": "TV",
"D": "HDR"
}
]
},
{
"A": "Sony",
"Groupe": [
{
"F": "T.V",
"D": "LCD"
},
{
"F": "PS4",
"D": "Pro edition"
},
{
"F": "Smart Phone",
"D": "Quad core"
}
]
}
]