I'm stuck trying to create nested multiple object json arrays with group by
birth_place in my rest api app, this is my code in controller:
$db = db_connect();
$data = [
'status' => true,
'error' => null,
'data' => $db->query('SELECT birth_place, COUNT(id) as jumlah_data_user, GROUP_CONCAT(id) as id, GROUP_CONCAT(name) as name, GROUP_CONCAT(birth_date) as birth_date, GROUP_CONCAT(gender) as gender FROM users GROUP BY birth_place')->getResultArray()
];
return $this->respond($data, 200);
and this is my result, the data is successfully grouped but the combined data is not separated into multidimension arrays with create new title as users, but instead becomes a combined string.
"status": true,
"error": null,
"data": [
{
"birth_place": "Administrasi Jakarta Barat",
"jumlah_data_user": "3",
"id": "716,764,922",
"name": "Elvina Nuraini,Rina Kezia Novitasari,Viktor Firmansyah M.Pd",
"birth_date": "1975-08-28,1988-06-07,1989-05-13",
"gender": "female,female,male"
},
{
"birth_place": "Administrasi Jakarta Pusat",
"jumlah_data_user": "2",
"id": "993,866",
"name": "Mursinin Banawa Marbun M.Pd,Ibrahim Gunawan",
"birth_date": "1991-12-27,1995-01-01",
"gender": "male,male"
},
];
this result i was expecting when project running :
"status": true,
"error": null,
"data": [
{
"birth_place" : "Administrasi Jakarta Barat",
"jumlah_data_user" : 3,
"users": [
{
"id": 716,
"name": "Elvina Nuraini",
"birth_date": "1975-08-28",
"gender": "female"
},
{
"id": 764,
"name": "Rina Kezia Novitasari",
"birth_date": "1988-06-07",
"gender": "female"
},
{
"id": 922,
"name": "Viktor Firmansyah M.Pd",
"birth_date": "1989-05-13",
"gender": "male"
}
]
},
{
"birth_place": "Administrasi Jakarta Pusat",
"jumlah_data_user" : 2,
"users": [
{
"id": 993,
"name": "Mursinin Banawa Marbun M.Pd",
"birth_date": "1991-12-27",
"gender": "male"
},
{
"id": 866,
"name": "Ibrahim Gunawan",
"birth_date": "1995-01-01",
"gender": "male"
}
]
},
];
How to do that?
To restructured your grouped data, you need to iterate the rows then explode and transpose the last four columns containing delimited strings. (Demo)
$data = array_map(
fn($row) => [
'birth_place' => $row['birth_place'],
'jumlah_data_user' => $row['jumlah_data_user'],
'users' => array_map(
fn(...$col) => array_combine(['id', 'name', 'birth_date', 'gender'], $col),
explode(',', $row['id']),
explode(',', $row['name']),
explode(',', $row['birth_date']),
explode(',', $row['gender'])
)
],
$this->Users_model->getGroupedByBirthPlace();
);
That said, there is some important advice to offer:
In a MVC framework, we should not see database queries executed from the controller. The controller's job for this task is to collect data from the model (where a method there will collect database data) and then return the response payload.
Grouping and concatenating in the SQL is not advisable for this task.
There are data length limits to these concatenated strings,
when values contain commas, then there is a delimiter conflict, and
you are unnecessarily increasing the workload for the database.
It would make your model method more re-reusable if you made the result set less niched. A good model has methods that can be used more than once throughout the project.
Try not to mix English with Malay (or other non-English languages). The project will be easier to manage and review if the language is consistent. I'll suggest group_total instead of jumlah_data_user.
With a simpler payload coming from your model's query, there is no need to make iterated explode&transpose processes. This is the more professional way to design your application. (Demo)
$grouped = array_values(
array_reduce(
$this->Users_model->get(), // call the method whatever you like
function($data, $row) {
$group = $row['birth_place'];
$data[$group] ??= [
'birth_place' => $row['birth_place'],
'group_total' => 0
];
++$data[$group]['group_total'];
unset($row['birth_place']);
$data[$group]['users'][] = $row;
return $data;
}
)
);
Related
Im making an app for a customer where the user can type in specifications of sold christmas trees, stored in a JSON format in the database as "line_items". It needs to be outputtet in a table on the delivery note for the customer to see. The user have cases where multiple pallets are loaded on to a truck with the same species of trees that share the same size. In that case I want to merge the duplicates and sum the amount.
I've come up with this:
$data = collect($deliveryNote->line_items)
->groupBy(['type', 'size']);
that gives me the output below.
{
"NGR": {
"125-150": [
{
"slot": 2,
"pallet": "cghjh",
"type": "NGR",
"label": "purple",
"size": "125-150",
"amount": "30"
},
{
"slot": 3,
"pallet": "cghjh",
"type": "NGR",
"label": "purple",
"size": "125-150",
"amount": "30"
}
],
"150-175": [
{
"slot": 2,
"pallet": "yghiuj",
"type": "NGR",
"label": "orange",
"size": "150-175",
"amount": "30"
}
]
},
"NOB": {
"125-150": [
{
"slot": 3,
"pallet": "cghjh",
"type": "NOB",
"label": "purple",
"size": "125-150",
"amount": "30"
}
]
}
}
This is grouped almost how i want.
Now i want to merge the duplicates, filter out the "slot", "pallet", "label" and update the amount for the collection to display like this:
{
"NGR": {
"125-150": [
{
"type": "NGR",
"size": "125-150",
"amount": "60"
},
],
"150-175": [
{
"type": "NGR",
"size": "150-175",
"amount": "30"
}
]
},
"NOB": {
"125-150": [
{
"type": "NOB",
"size": "125-150",
"amount": "30"
}
]
}
}
I've tried this:
$data = collect($deliveryNote->line_items)
->groupBy(['type', 'size']);
->map(function ($item) {
return $item->sum('amount');
});
And it's not giving me the correct output.
I hope anybody has the the time to help me out.
Thanks you in advance.
In your last example, you are only iterating the "NGR", "NOB" keys and therefor values, to get to the trees you want to sum, you have to go one level deeper in your iteration.
Having a hard time figuring out if line_items is a relationship or custom data set, assumptions written in comments.
$data = collect($deliveryNote->line_items)
->groupBy(['type', 'size']);
->map(function ($tree) {
// i assume $trees is an array else don't wrap
return collect($trees)->map(function ($shipments) {
return [
'amount' => $shipments->sum('amount'),
// assuming object, if array access it like so
'size' => $shipments->first()->size,
'amount' => $shipments->first()->amount,
];
});
});
I just started mondoDB a month back and I am currently working to join/pull data from multiple collections (Just like extracting data from a foreign table where the primary table has a foreign key)
I have below collection dump in products collection
{
"_id": {
"$oid": "600aef5d01290000270051e4"
},
"user_id": "600adb7f01290000270051df",
"details": {
"name": "My product",
"description": "some prod description goes here",
"category": "5fef4a467d1b000086000745"
},
"images": [{
"size_100x128": "prods/2021/Jan/prod_ab679096373ab328bf454447abc304c5_100x128.jpeg",
"size_200x256": "prods/2021/Jan/prod_ab679096373ab328bf454447abc304c5_200x256.jpeg",
"size_300x385": "prods/2021/Jan/prod_ab679096373ab328bf454447abc304c5_300x385.jpeg",
"size_500x642": "prods/2021/Jan/prod_ab679096373ab328bf454447abc304c5_500x642.jpeg"
}],
"attributes": {
"for": ["5fef71907d1b000086000761", "5fef71907d1b000086000761"],
"colors": ["5fef719e7d1b000086000763", "5fef719e7d1b000086000763", "5fef719e7d1b000086000763"],
"sizes": [{
"product_size_id": "5fef716d7d1b00008600075b",
"quantity": "9"
}, {
"product_size_id": "5fef716d7d1b00008600075b",
"quantity": "1"
}]
},
"total_quantity": 10,
"created": "2021-01-22 20:59:33",
"modified": "2021-01-22 22:54:16",
}
From the above dump, you can see that the attributes section has a lot of IDs that reside to data in other tables. I want to execute a single query that can give me a name related to that ID from foreign Table
I saw a lot of posts from StackOverflow [this was the last that I saw][1] then i somehow got to make below code
$productsArray = $collection->aggregate(
[
[
'$match' => [
'_id' => new MongoDB\BSON\ObjectID( $product_id )
]
],
[
'$lookup' => [
'from' => 'product_for',
'localField' => 'attributes.for',
'foreignField' => '_id',
'as' => 'productFor'
]
],
// [
// '$unwind' => '$productFor'
// ],
]
);
I commented unwind because if I use it then it does now show all data but when I uncomment it I get all data but I am not able to get data from foreign fields. I have almost banged my head everywhere and stuck on how to make it happen.
Below is the collection dump from product_for
{
"_id": {
"$oid": "5fef71907d1b000086000761"
},
"created_console_id": {
"$oid": "5f9af88dad3ebff51648211d"
},
"name": "Gents",
"created": "2021-01-02 00:31:36",
"modified": "2021-01-02 00:31:36",
"last_modified_by": {
"$oid": "5f9af88dad3ebff51648211d"
}
}
It would be great if someone can help mere
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 (
),
),
),
),
),
),
),
),
)
Am using Yii2 AuthManager to get a persions permissions using
return [
"permissions" => \Yii::$app->authManager->getPermissionsByUser(
Yii::$app->user->identity->id ),
]
The above returns data of the form
"permissions": {
"permission1":{
"type": "2",
"name": "permission1",
},
"permission2":{
"type": "2",
"name": "permission2",
}
..................
}
What i was looking for is an outpiut like this
"permissions": {
0:{
"type": "2",
"name": "permission1",
},
1:{
"type": "2",
"name": "permission2",
}
..................
}
How do i convert the above to an array of values not with the keys to get the desired output?
Try this:
return [
"permissions" => array_values(\Yii::$app->authManager->getPermissionsByUser(
Yii::$app->user->identity->id )),
];
Assuming you get the result in $permissions
foreach ($permissions as $key => $value){
$new_perm[] = $value;
}
in $new_perm you should obtain the result you need
I have a multidimensional array, i wish to extract each value from this array.
The array is stored in $data.
{"success":true,"categories":
[
{"
category_id":"C1",
"parent_id":"P1",
"name":"N1",
"categories":
[
{
"category_id":"C11",
"parent_id":"P11",
"name":"N11",
},
{
"category_id":"C12",
"parent_id":"P12",
"name":"N12",
},
],
"status":"1"
},
{
category_id":"C2",
"parent_id":"P2",
"name":"N2",
"categories":
[
{
"category_id":"C21",
"parent_id":"P21",
"name":"N21",
[
{
"category_id":"C22",
"parent_id":"P23",
"name":"N24",
}
],
"status":"2"
}
],
"status":"3"
},
]
}
I tried using
$total = $data['categories']['category_id'];
to fetch value C11
but wasn't able to do so.
can anyone tell how i can fetch all the data especially C22
You have to first use json_decode.
$array = json_decode($data, true);
Then you can access the array as you have stated.
Or loop throught the categories:
if (!empty($array)) {
foreach ($array['categories'] as $category) {
echo $category['id'];
}
}
You may have to do this recursively to loop through the categories within the categories. But it depends completely what you want to achieve. A nested loop could do the job if it is always just one level deep.
EDIT
The JSON you have provided is not quite right, I have given a corrected one below:
{
"success": true,
"categories": [
{
"category_id": "C1",
"parent_id": "P1",
"name": "N1",
"categories": [
{
"category_id": "C11",
"parent_id": "P11",
"name": "N11"
},
{
"category_id": "C12",
"parent_id": "P12",
"name": "N12"
}
],
"status": "1"
},
{
"category_id": "C2",
"parent_id": "P2",
"name": "N2",
"categories": [
{
"category_id": "C21",
"parent_id": "P21",
"name": "N21",
"categories": [
{
"category_id": "C22",
"parent_id": "P23",
"name": "N24"
}
],
"status": "2"
}
],
"status": "3"
}
]
}
There were a few trailing commas and missing quote marks.
The data is not in PHP array, its a JSON array. You have to decode it, by using json_decode() function.
That's JSON, not php multidimensional array. You can use json_decode function to read through it.