Get sum of objects with same id inside nested array php - php

This is my array of objects in php:
Array( [0]=>
Array (
[added_time] => 2018-12-09 14:00:00+00
[id] => 123
[places] =>
[{
"place_id" : 1,
"total" : 5,
"empty" : 0,
"weight" : 68000,
"persons" :
[{
"person_id" : 2,
"person_name" : "ABC",
"total":100
},
{
"person_id" : 3
"person_name" : "DEF",
"total":200
}]
},
"place_id" : 4,
"total" : 10,
"empty" : 0,
"weight" : 54000,
"persons" :
[{
"person_id" : 2,
"person_name" : "ABC"
"total":100
},
{
"person_id" : 6
"person_name" : "GHI",
}
]
],
),
Array (
[added_time] => 2018-12-09 15:00:00+00
[id] => 456
[places] =>
[{
"place_id" : 1,
"total" : 5,
"empty" : 0,
"weight" : 68000,
"persons" :
[{
"person_id" : 2,
"person_name" : "ABC",
"total":200
},
{
"person_id" : 3
"person_name" : "DEF",
"total":300
}]
}]
)
)
I am trying to get sum on the base of person_id like group by person_id.
My desired result should be:
Array (
[added_time] => 2018-12-09 14:00:00+00
[id] => 123
persons:array(
Array([0]=>
[person_id] : 2,
[person_name] : "ABC",
[total]:200
),
Array([1]=>
[person_id] : 3,
[person_name] : "DEF",
[total]:300
),
Array([2]=>
[person_id] : 6,
[person_name] : "GHI",
[total]:500
)
),
[added_time] => 2018-12-09 15:00:00+00
[id] => 123
persons:array(
Array([0]=>
[person_id] : 2,
[person_name] : "ABC",
[total]:200
),
Array([1]=>
[person_id] : 3,
[person_name] : "DEF",
[total]:300
)
)
)
How can I achieve this result. I can use foreach but I don't want to use it because of three iterations.
Is there any other method to achieve this in php and without any performance issue?

You can have that by using PHP function as array-merge, array-column, array-reduce.
Let divided that to 3 step:
1.Sum total for all person given list of persons. Define the the following reduce function:
function reduce($carry, $item)
{
if (!in_array($item["person_name"], array_column($carry, "person_name")))
$carry[] = $item;
else {
foreach($carry as &$person) {
if ($item["person_name"] == $person["person_name"])
$person["total"] += $item["total"];
}
}
return $carry;
}
2.For every element in your array create you output using the reduce from step 1:
function reduceElement($arr) {
$res = array("added_time" => $arr["time"], "id" => $arr["id"]);
$persons = array();
foreach($arr["places"] as $place) {
$persons = array_merge($persons, $place["persons"]);
}
$res["persons"] = array_reduce($persons, "reduce", array());
return $res;
}
3.Combine it all:
$elemA= array("id"=>1, "time"=>"2018", "places" => array(array("persons" => array(array("person_name" => "ABC", "total" => 100), array("person_name" => "DEF", "total" => 200))), array("persons" => array(array("person_name" => "ABC", "total" => 100), array("person_name" => "GHI", "total" => 100)))));
$elemB = array("id"=>2, "time"=>"2017", "places" => array(array("persons" => array(array("person_name" => "ABC", "total" => 200), array("person_name" => "DEF", "total" => 300)))));
$arr = array($elemA, $elemB);
$res = array();
foreach($arr as $obj)
$res[] = reduceElement($obj);
print_r($res);
This will give you the require out put

Related

Transpose subarray data from indexed to associative

I'm making an API for getting some data. My API gives object data like given, given object I wanted to format some data inside object:
{
"data": [
{
"productId": 55,
"productTitle": "Test product",
"variation": {
"Color": "Red",
"Size": "XS",
"din": "10190537",
"product_id": 55,
"name": [
"Color",
"Size"
],
"value": [
"Red",
"XS"
]
},
"din": "10190537",
"markets": [
{
"id": 11,
"name": "paytmmall",
"displayName": "PayTm Mall",
"identifierName": "Product_Id"
}
]
}
]
}
In this object I want data like given
{
"data": [
{
"productId": 55,
"productTitle": "this is test from hariom",
"variation": {
"Color": "Red",
"Size": "XS",
"din": "10190537",
"product_id": 55,
"variationTypes": [
{
"name": "Color",
"value": "Red"
},
{
"name": "Size",
"value": "XS"
}
],
},
"din": "10190537",
"markets": [
{
"id": 11,
"name": "paytmmall",
"displayName": "PayTm Mall",
"identifierName": "Product_Id"
}
]
}
]
}
Here Is my Controller Name
public function MarketMapping(Request $request)
{
$sellerId = Auth::guard('seller-api')->user();
$page = $request->has('pageNumber') ? $request->get('pageNumber') : 1;
$limit = $request->has('perPage') ? $request->get('perPage') : 10;
$variationFromInvTbl = ProductInventory::select('Color', 'Size', 'din', 'product_id')->where('seller_id', $sellerId->id)->where('status', 'active')->limit($limit)->offset(($page - 1) * $limit)->get();
$dataArray = array();
foreach($variationFromInvTbl as $key => $varitionValue)
{
$prodtsFromLivetbl = ProductsLive::select('productTitle', 'product_id')->where('product_id', $varitionValue->product_id)->get();
foreach ($prodtsFromLivetbl as $key => $value)
{
$marketChannelData = DB::table('market_channels')
->join('sellers_market_channels', 'market_channels.name', '=', 'sellers_market_channels.key')
//->join('market_product_mappings', 'market_channels.id', '=', 'market_product_mappings.market_id')
->select('market_channels.id','market_channels.name', 'market_channels.displayName','market_channels.identifierName') //'market_product_mappings.identifierValue'
->where('sellers_market_channels.seller_id', $sellerId->id)
->where('sellers_market_channels.value', 1)
->get();
$maketProductMap = MarketProductMapping::where('seller_id', $sellerId->id)->where('product_id', $varitionValue->product_id)->where('din', $varitionValue->din)->pluck('identifierValue');
if (count($maketProductMap))
{
$marketChannelData[$key]->value = $maketProductMap[0];
}
$varitionValue['name']= array_keys($varitionValue->only(['Color', 'Size']));
$varitionValue['value'] = array_values($varitionValue->only(['Color', 'Size']));
$dataObject = ((object)[
"productId" => $value->product_id,
"productTitle" => $value->productTitle,
"variation" => $varitionValue,
"din" => $varitionValue['din'],
"markets" => $marketChannelData
]);
array_push($dataArray,$dataObject);
}
}
if($variationFromInvTbl)
{
$response['success'] = true;
$response["page"] = $page;
$response["itemPerPage"] = $limit;
$response["totalRecords"] = $this->CountMarketMapping($page, $limit, $sellerId->id);
$response['data'] = $dataArray;
return response()->json($response, 200);
}else{
$response['success'] = false;
$response['data'] = $prodtsFromLivetbl;
return response()->json($response, 409);
}
}
You are using laravel's only() method which returns an associative array.
You wish to convert each key-value pair into a subarray containing two associative elements -- the original key will be the value of the name element
and the original value will be the value of the value element.
By passing the original array keys and array values into array_map(), you can iterate them both synchronously.
compact() is a perfect native function to create the desired associative subarrays from the iterated parameters.
Code: (Demo)
$variations = $varitionValue->only(['Color', 'Size']);
$dataObject = (object)[
// ... your other data
'variations' => array_map(
function($name, $value) {
return compact(['name', 'value']);
},
array_keys($variations),
$variations
),
// ...your other data
];
var_export($dataObject);
Output:
(object) array(
'variations' =>
array (
0 =>
array (
'name' => 'Color',
'value' => 'Red',
),
1 =>
array (
'name' => 'Size',
'value' => 'XS',
),
),
)
This script will help you
<?php
$data = [
"variation" => [
[
"Color" => "Red",
"Size" => "XS",
"din" => "10190537",
"product_id" => 55,
"name" => [
"0" => "Color",
"1" => "Size"
],
"value" => [
"0" => "Red",
"1" => "XS"
]
]
]
];
for ($i=0; $i < count($data["variation"]); $i++) {
$data["variation"][$i]["data"]["name"] = $data["variation"][$i]["name"];
$data["variation"][$i]["data"]["value"] = $data["variation"][$i]["value"];
unset($data["variation"][$i]["name"]);
unset($data["variation"][$i]["value"]);
}
print_r($data);
output
Array
(
[variation] => Array
(
[0] => Array
(
[Color] => Red
[Size] => XS
[din] => 10190537
[product_id] => 55
[data] => Array
(
[name] => Array
(
[0] => Color
[1] => Size
)
[value] => Array
(
[0] => Red
[1] => XS
)
)
)
)
)

Return result of recursive function

I got a recursive function which currently echo the results. I want this function to return results and loop them with foreach inside markup.
I understand that i should use some kind of iteration for each array to get my desired result but have failed with that. Currently my code look like this(with attempts to iterate):
public static function recursiveChildCategory($categories = array(), $depth = 0, $i = 0) {
$ca = [];
// Loop through categories
foreach($categories as $key => $category){
echo str_repeat(" ", $depth);
echo "<a href='".implode('/', $category['breadcrumb'])."'>{$category['title']}</a>";
echo '<br>';
$ca[$i] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
self::recursiveChildCategory($category['child'], $depth + 1, $i++);
}
}
return $ca;
}
And incoming array to the function:
Array (
[0] => Array (
[id] => 7
[title] => Deserts
[slug] => deserts
[child] => Array (
[0] => Array (
[id] => 8
[title] => Space
[slug] => space
[child] => Array (
[0] => Array (
[id] =>
[title] =>
[slug] =>
[child] => Array ( )
)
)
)
)
)
)
Currently it just returns first level of child categories "Deserts", but nothing about "Space".
As desired result i want function to return all categories with $depth and infinite path to multiple child categoires (to do the same work as currently echo doing).
Thanks in advice
Try this and tell me if it's what you are looking for :
The array for my test :
$array = [
0 => [
"id" => 7,
"title" => "Deserts",
"slug" => "deserts",
"child" => [
0 => [
"id" => 8,
"title" => "Space",
"slug" => "space",
"child" => [
0 => [
"id" => 9,
"title" => "Test",
"slug" => "test"
]
]
]
]
]
];
The recursive function :
function recursiveChildCategory($categories, $depth = 0, $ca = []) {
// Loop through categories
foreach($categories as $key => $category){
$ca[$depth] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
$ca = recursiveChildCategory($category['child'], $depth + 1, $ca);
} else {
break;
}
}
return $ca;
}
Now, how to use it :
$test = recursiveChildCategory($array);
var_dump($test);
And this is the output :
array(3) {
[0]=>
array(2) {
["id"]=>
int(7)
["title"]=>
string(7) "Deserts"
}
[1]=>
array(2) {
["id"]=>
int(8)
["title"]=>
string(5) "Space"
}
[2]=>
array(2) {
["id"]=>
int(9)
["title"]=>
string(4) "Test"
}
}
Here is a link to test it : http://sandbox.onlinephpfunctions.com/
EDIT : I made some modification because in OP example array can have multiple result in first "depth", here is the "new" solution :
The array for test :
$array = [
"0" =>
[ "id" => 3, "title" => "Subcat", "slug" => "subcat", "child" =>
[ "0" =>
[ "id" => 5, "title" => "Subsubcat2", "slug" => "subcat2", "child" =>
[ "0" =>
[ "id" => "", "title" => "", "slug" =>"", "breadcrumb" => [ "0" => "homeworld", "1" => "cat", "2" => "subcat", "3" => "subcat2" ], "child" => [ ] ]
]
]
]
],
"1" =>
[ "id" => 4, "title" => "Kalahari", "slug" => "kalahari", "child" =>
[ "0" => [ "id" => 7, "title" => "deserts", "slug" => "deserts", "child" =>
[ "0" =>
[ "id" => 8, "title" => "Ural", "slug" => "ural", "child" =>
[ "0" => [ "id" =>"", "title" =>"", "slug" =>"", "child" => [ ] ] ]
]
]
]
]
]
];
The function : I just add $ca[$depth][] instead of $ca[$depth]
function recursiveChildCategory($categories, $depth = 0, $ca = []) {
// Loop through categories
foreach($categories as $key => $category){
$ca[$depth][] = [
'id' => $category['id'],
'title' => $category['title'],
];
if(isset($category['child'])) {
// Loop
$ca = recursiveChildCategory($category['child'], $depth + 1, $ca);
} else {
break;
}
}
return $ca;
}
And now the result :
$test = recursiveChildCategory($array);
foreach ($test as $depth => $c) {
echo "depth : ".$depth."\n";
foreach ($c as $result) {
echo "Title : ".$result['title']."\n";
}
echo "=============\n";
}
The output is :
depth : 0
Title : Subcat
Title : Kalahari
=============
depth : 1
Title : Subsubcat2
Title : deserts
=============
depth : 2
Title :
Title : Ural
=============
depth : 3
Title :
=============
Test here : link

PHP Add null to array value if it doesn't exist

I'm generating data arrays and want to add 0/null to a specific month, where no data is present, but been struggling with it for few days.
I'm generating the final dataset array like this
foreach ($patients as $key => $item) {
$labels[] = $item['month'];
usort($labels, "compare_months");
$labels = array_unique($labels);
$chartData[ $item['brand_name'] ]['data'][] =
$item['patientsCount'];
$chartData[ $item['brand_name'] ]['brand_data'] = [
"name" => $item['brand_name'],
"color" => $item['color']
];
}
foreach ($chartData as $item) {
$pointRadius++;
$dataSet[] = [
'label' => $item['brand_data']['name'],
'data' => $item['data'],
'pointRadius' => $pointRadius,
'fill' => false,
'borderWidth' => 1,
'backgroundColor' => "#" . $item['brand_data']['color'],
'borderColor' => "#" . $item['brand_data']['color'],
];
}
$finalData[] = [
'labels' => $labels,
'datasets' => $dataSet
];
Which gives me this json response
[
{
"labels":[
"April",
"May",
"June",
"July"
],
"datasets":[
{
"label":"Medicine 1",
"data":[
1
],
"pointRadius":1,
"fill":false,
"borderWidth":1,
"backgroundColor":"#ea5f2d",
"borderColor":"#ea5f2d"
},
{
"label":"Medicine 2",
"data":[
1,
1,
1
],
"pointRadius":2,
"fill":false,
"borderWidth":1,
"backgroundColor":"#ffb400",
"borderColor":"#ffb400"
},
{
"label":"Medicine 3",
"data":[
1,
1,
2
],
"pointRadius":3,
"fill":false,
"borderWidth":1,
"backgroundColor":"#ff7777",
"borderColor":"#ff7777"
},
{
"label":"Medicine 4",
"data":[
1,
1,
2
],
"pointRadius":4,
"fill":false,
"borderWidth":1,
"backgroundColor":"#64a36f",
"borderColor":"#64a36f"
},
{
"label":"Medicine 5",
"data":[
2
],
"pointRadius":5,
"fill":false,
"borderWidth":1,
"backgroundColor":"#e7e6fc",
"borderColor":"#e7e6fc"
}
]
}
]
As you can see, for some items, the data object only contains 1 value which is wrong, because I need to add zeros to months, where there is no data.
Can this be done in MySQL or is it better to use PHP and how?
EDIT 1
Here is the array I am currently getting.
Array
(
[MEDICINE 1] => Array
(
[April] => 1
)
[MEDICINE 2] => Array
(
[April] => 1
[July] => 2
)
[MEDICINE 3] => Array
(
[April] => 1
[May] => 1
[July] => 2
)
[MEDICINE 4] => Array
(
[May] => 1
[July] => 3
)
[MEDICINE 5] => Array
(
[June] => 2
)
[MEDICINE 6] => Array
(
[July] => 1
)
)
As you can see some medicines have data for only one month and I want to add these months to the medicine array with 0 value, if there is no data.
Add this line after each foreach: if (count($item['data']) < 2) continue;. Because you need skip if length of data is smaller than 2.
foreach ($patients as $key => $item) {
if (count($item['data']) < 2) continue;
$labels[] = $item['month'];
usort($labels, "compare_months");
$labels = array_unique($labels);
$chartData[ $item['brand_name'] ]['data'][] =
$item['patientsCount'];
$chartData[ $item['brand_name'] ]['brand_data'] = [
"name" => $item['brand_name'],
"color" => $item['color']
];
}
foreach ($chartData as $item) {
$pointRadius++;
if (count($item['data']) < 2) continue;
$dataSet[] = [
'label' => $item['brand_data']['name'],
'data' => $item['data'],
'pointRadius' => $pointRadius,
'fill' => false,
'borderWidth' => 1,
'backgroundColor' => "#" . $item['brand_data']['color'],
'borderColor' => "#" . $item['brand_data']['color'],
];
}
$finalData[] = [
'labels' => $labels,
'datasets' => $dataSet
];

How to find the average rating based on item name

I am working on building simple recommendation engine using PHP and MongoDB. I am trying to find the average rating of each item but I did not figure it out yet.
my collection is:
{
"_id" : 1,
"item" : "efg",
"rating" : 5.0,
"date" : ISODate("2014-01-01T08:00:00.000Z")
}
{
"_id" : 2,
"item" : "efg",
"rating" : 2.0,
"date" : ISODate("2014-01-01T08:00:00.000Z")
}
{
"_id" : 3,
"item" : "abc",
"rating" : 5.0,
"date" : ISODate("2014-01-01T08:00:00.000Z")
}
{
"_id" : 4,
"item" : "abc",
"rating" : 4.0,
"date" : ISODate("2014-01-01T08:00:00.000Z")
}
{
"_id" : 5,
"item" : "xyz",
"rating" : 3.0,
"date" : ISODate("2014-01-01T08:00:00.000Z")
}
This is the code I have tried recently:
$out = $sales->aggregate(
array(
'$group' => array(
'_id' => '$item',
'avgrating' => array('$avg' => '$rating')
)
)
);
var_dump($out);
Please try executing following code snippet
$out = $sales->aggregate(
array(
'$group' => array(
'_id' => array('item' => '$item'),
'avgrating' => array('$avg' => '$rating')
)
)
);
var_dump($out);
I got it working using this code:
$pipeline = array(
array(
'$group' => array(
'_id' => '$itemId',
'avgrating' => array('$avg' => '$rating'),
),
)
);
$out = $sales1->aggregate($pipeline);
var_dump($out)

how to create a pivot array in php

I have the following :
$data = [
{model_num : "ABC", revision : "AA", "testRecipeID":85, value : 31.25, treatment : 'Pressure' },
{model_num : "ABC", revision : "AA", "testRecipeID":85, value : 31.25, treatment : 'Gas' },
{model_num : "ABC", revision : "AA", "testRecipeID":85, value : 33.12, treatment : 'Temp' },
{model_num : "ABC", revision : "AA", "testRecipeID":85, value : 25.87, treatment : 'Current' },
{model_num : "ABC", revision : "AB", "testRecipeID":86, value : 26.63, treatment : 'Pressure' },
{model_num : "ABC", revision : "AB", "testRecipeID":86, value : 26.00, treatment : 'Gas' },
{model_num : "ABC", revision : "AB", "testRecipeID":86, value : 23.75, treatment : 'Temp' }
];
and i would like to end up with something like this:
var data=[{model_num : "ABC", revision : "AA", "testRecipeID":85, "Pressure":31.25, "Gas":31.25, "Temp": 33.12,"Current":25.87 },{model_num : "ABC", revision : "AB", "testRecipeID":86, "Gas":26.00,"Temp":23.75}]
I know how to do this in JS but not on PHP and it turns out I need to do it in my php so that I can process the large amounts of data that I have. I have this based on another question that I found but It doesn't work it returns a 0
$table = array();
$round_names = array();
$total = array();
foreach ($data as $score)
{
$round_names[] = $score->treatment;
$table[$score->testRecipeID][$score->treatment] = $score->value;
$total[$score->testRecipeID] += $score->value;
print_r($round_names);
}
$round_names = array_unique($round_names);
foreach ($table as $player => $rounds)
{
echo "$player\t";
foreach ($round_names as $round)
echo "$rounds[$round]\t";
echo "$total[$player]\n";
}
any help will be greatly appreciated!
this is how i do it in JS
var result = [];
data.forEach(function(e) {
var a = e.model_num + '|' + e.revision+ '|'e.recipeID;
if(!this[a]) {
this[a] = {model_num: e.model_num, revision: e.revision, recipeID: e.recipeID}
result.push(this[a]);
}
this[a][e.treatment] = e.value;
}, {});
If you need the structure, you can try with JSON Encode:
<?php
$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5);
echo json_encode($arr);
?>
Which outputs:
{"a":1,"b":2,"c":3,"d":4,"e":5}
If you need it to be an array, use:
echo json_encode(array($arr));
This is your JS function in PHP
<?php
$data = '[
{"model_num" : "ABC", "revision" : "AA", "testRecipeID":85, "value" : 31.25, "treatment" : "Pressure" },
{"model_num" : "ABC", "revision" : "AA", "testRecipeID":85, "value" : 31.25, "treatment" : "Gas" },
{"model_num" : "ABC", "revision" : "AA", "testRecipeID":85, "value" : 33.12, "treatment" : "Temp" },
{"model_num" : "ABC", "revision" : "AA", "testRecipeID":85, "value" : 25.87, "treatment" : "Current" },
{"model_num" : "ABC", "revision" : "AB", "testRecipeID":86, "value" : 26.63, "treatment" : "Pressure" },
{"model_num" : "ABC", "revision" : "AB", "testRecipeID":86, "value" : 26.00, "treatment" : "Gas" },
{"model_num" : "ABC", "revision" : "AB", "testRecipeID":86, "value" : 23.75, "treatment" : "Temp" }
]';
$data = json_decode($data);
$result = [];
foreach ($data as $row) {
$a = $row->model_num . '|' . $row->revision . '|' . $row->testRecipeID;
if (! array_key_exists($a, $result)) {
$result[$a] = [
'model_num' => $row->model_num,
'revision' => $row->revision,
'testRecipeID' => $row->testRecipeID
];
}
}
Because neither of the previous answers managed to provide the desired output, I am compelled to answer this pivot question.
Create a temporary string from the 3 identifying column values.
Use that string as the first level key while grouping related row data.
The null coalescing assignment operator spares you needing to call isset() to check if the group has been encountered before. If it is the first encounter, save the three core elements to the row.
Unconditionally push the dynamically keyed value into the group's row.
5 When finished iterating clear away the temporary keys with array_values().
Code: (Demo)
$data = [
['model_num' => 'ABC', 'revision' => 'AA', 'testRecipeID' => 85, 'value' => 31.25, 'treatment' => 'Pressure'],
['model_num' => 'ABC', 'revision' => 'AA', 'testRecipeID' => 85, 'value' => 31.25, 'treatment' => 'Gas'],
['model_num' => 'ABC', 'revision' => 'AA', 'testRecipeID' => 85, 'value' => 33.12, 'treatment' => 'Temp'],
['model_num' => 'ABC', 'revision' => 'AA', 'testRecipeID' => 85, 'value' => 25.87, 'treatment' => 'Current'],
['model_num' => 'ABC', 'revision' => 'AB', 'testRecipeID' => 86, 'value' => 26.63, 'treatment' => 'Pressure'],
['model_num' => 'ABC', 'revision' => 'AB', 'testRecipeID' => 86, 'value' => 26.0, 'treatment' => 'Gas'],
['model_num' => 'ABC', 'revision' => 'AB', 'testRecipeID' => 86, 'value' => 23.75, 'treatment' => 'Temp']
];
$result = [];
foreach ($data as $row) {
$compositeKey = "{$row['model_num']}-{$row['revision']}-{$row['testRecipeID']}";
$result[$compositeKey] ??= [
'model_num' => $row['model_num'],
'revision' => $row['revision'],
'testRecipeID' => $row['testRecipeID']
];
$result[$compositeKey][$row['treatment']] = $row['value'];
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'model_num' => 'ABC',
'revision' => 'AA',
'testRecipeID' => 85,
'Pressure' => 31.25,
'Gas' => 31.25,
'Temp' => 33.12,
'Current' => 25.87,
),
1 =>
array (
'model_num' => 'ABC',
'revision' => 'AB',
'testRecipeID' => 86,
'Pressure' => 26.63,
'Gas' => 26.0,
'Temp' => 23.75,
),
)

Categories