Laravel collection merge from array based on item key value - php

I have an array of $dates like below
[
"2022-30",
"2022-31",
"2022-32",
"2022-33",
"2022-34",
"2022-35",
]
and I have a $collection with an output like below
[
{
"new": 60,
"settled": "1",
"date": "2022-31"
},
{
"new": 50,
"settled": "1",
"date": "2022-32"
},
]
how can I achieve a result like below which merge the value of date from the $collection item if it matches from the date on $dates array
[
{
"new": 0,
"settled": "0",
"date": "2022-30"
},
{
"new": 60,
"settled": "1",
"date": "2022-31"
},
{
"new": 50,
"settled": "1",
"date": "2022-32"
},
{
"new": 0,
"settled": "0",
"date": "2022-33"
},
{
"new": 0,
"settled": "0",
"date": "2022-34"
},
{
"new": 0,
"settled": "0",
"date": "2022-35"
}
]
I tried making the dates into a collection and formatting the output like the $collection format and use merge but this just combine the two collection together with duplicates.
$out = collect($dates)->map( function($d, $k) {
return [
'new' => 0,
'settled' => 0,
'date' => $d
];
});
return $out->merge($collection);
appreciate any help

$dates = [
"2022-30",
"2022-31",
"2022-32",
"2022-33",
"2022-34",
"2022-35",
];
$collection = collect([
[
"new" => 60,
"settled" => "1",
"date" => "2022-31"
],
[
"new" => 50,
"settled" => "1",
"date" => "2022-32"
]
]);
foreach ($dates as $date) {
if (!$collection->where('date', $date)->count()) {
$collection->push([
'new' => 0,
'settled' => 0,
'date' => $date
]);
}
}
return $collection->sortBy('date')->values()->all();

You can key the collection by dates and then add in the dates that are not in there:
$out = $collection->keyBy('date')
$out = $out->merge(
collect($dates)
->whereNotIn(null, $out->keys())
->mapWithKeys(fn ($date) => [
$date => [
'new' => 0,
'settled' => 0,
'date' => $date
]
])
)->sortKeys()->values();
What this does is convert the collection to one that has the dates as keys, then filters from dates all the dates that are already in the collection, maps the rest into your desired format, sorts them all to how they should be then discards the keys.

For the $dates maybe you can filter only the dates that does not exist in the collections? Then after you can map them out and merge.

Related

add values ​from a collection to others using a column as a reference (PHP, LARAVEL)

I am working with php in laravel, in this case I have 2 collections of objects, one of them looks like this:
[
{
"id": 1,
"name": "product x",
"quantity": "100",
},
{
"id": 2,
"codProd": "product y",
"quantity": "200",
},
{
"id": 3,
"name": "product a.",
"quantity": "30",
}
]
and the other looks like this:
[
{
"reference": 1,
"quantity": "80",
},
{
"reference": 2,
"quantity": "50",
},
]
What I need is to keep the first collection but adding the value of the quantity key from the second collection, using the reference key as a relationship with the id of the first collection, the final result should look like this:
[
{
"id": 1,
"name": "product x",
"quantity": "180",
},
{
"id": 2,
"codProd": "product y",
"quantity": "250",
},
{
"id": 3,
"name": "product a.",
"quantity": "30",
}
]
so how can I do this?, any guide or help I am grateful
You can use a collection method for this, the map method:
$collection_one = collect([
[
'id' => 1,
'name' => 'product x',
'quantity' => 100,
],
[
'id' => 2,
'name' => 'product y',
'quantity' => 100,
],
[
'id' => 1,
'name' => 'product a.',
'quantity' => 30,
],
]);
$collection_two = collect([
[
'reference' => 1,
'quantity' => 80,
],
[
'reference' => 2,
'quantity' => 50,
],
]);
For you to get the collection what you want:
$collect = [];
$collect = $collection_one->map(function ($value_one) use ($collection_two) {
if ($search = $collection_two->firstWhere('reference', '=', $value_one['id'])) {
$value_one['quantity'] += $search['quantity'];
}
return $value_one;
});
Ready, that worked for me. :)

How to prevent duplication of object inside by pushing it to the array in php?

I have question, is it possible not to duplicate the array object by looping on it? Right now I used laravel as my backend
I have here my response which is the exchange object duplicate itself.
[
{
"exchange": {
"id": 1,
"branch": "BB1",
"old_check_no": "0001",
"cash": "250000",
"bank_deposit": "1000000",
"offset": "250000",
"amount": "10000",
"over_under": null,
"checkDate": "2021-09-11",
"remarks": "1",
"date_closed": "2021-09-11"
},
"exchange_list": {
"exchange_id": 1,
"new_check_no": "001",
"new_check_bank": "bank",
"new_check_branch": "Lagros"
}
},
{
"exchange": {
"id": 1,
"branch": "BB1",
"old_check_no": "0001",
"cash": "250000",
"bank_deposit": "1000000",
"offset": "250000",
"amount": "10000",
"over_under": null,
"checkDate": "2021-09-11",
"remarks": "1",
"date_closed": "2021-09-11"
},
"exchange_list": {
"exchange_id": 1,
"new_check_no": "002",
"new_check_bank": "bank",
"new_check_branch": "Lagros"
}
},
]
Now my goal is to push the exchange without duplication:
[
{
"exchange": {
"id": 1,
"branch": "BB1",
"old_check_no": "0001",
"cash": "250000",
"bank_deposit": "1000000",
"offset": "250000",
"amount": "10000",
"over_under": null,
"checkDate": "2021-09-11",
"remarks": "1",
"date_closed": "2021-09-11"
},
"exchange_list": {
"exchange_id": 1,
"new_check_no": "001",
"new_check_bank": "bank",
"new_check_branch": "Lagros"
},
"exchange_list": {
"exchange_id": 1,
"new_check_no": "002",
"new_check_bank": "bank",
"new_check_branch": "Lagros"
}
}
]
Here is what my foreach loop like and how i push the array object.
$myArray = [];
foreach($exchange_check as $primary_array) {
foreach($exchange_lists as $second_array) {
if($second_array->exchange_id == $primary_array->id) {
array_push($myArray, (object)[
'exchange' => $primary_array,
'exchange_list' => $second_array,
]);
}
}
}
Thanks
You should add exchange data to array only once in first loop:
$myArray = [];
foreach($exchange_check as $primary_array) {
$idx = array_push($myArray, ['exchange' => $primary_array]);
foreach($exchange_lists as $second_array) {
if($second_array->exchange_id == $primary_array->id) {
//array_push() returns the new number of elements in the array,
//to get currently added array element we should subtract 1 from this number
$myArray[$idx-1]['exchange_list'][] = $second_array;
}
}
}
And in second loop add only exchange_list data to array.

return array in PHP, Symfony 4 for REST API

I create my api restoration in symfony 4, but the array I return does not look like I want to.
Below I present my code, resultalt that is currently being performed and at the bottom is the result that I am trying to do.
Can I ask someone for help? I am not entirely sure how to achieve this effect.
This is my service php:
$data = $repository->findByData($fromDate, $toDate);
$i = 1;
$j = 1;
$resultArray = [];
foreach ($data as $key => $d) {
$date = $d['date'];
$dateString = $date->format('Y-m-d');
$resultIteration[$key] = [
'customerID' => $d['id'],
'customerName' => $d['customerName'],
'data' => [
'ordersAmount' . $i++ => $d['ordersAmount'],
'earnings' . $i++ => $d['earnings'],
'date' => $dateString,
],
'ordersAmount' . $j++ => $d['ordersAmount'],
'earnings' . $j++ => $d['earnings'],
];
array_push($resultArray,$resultIteration[$key]);
}
return $resultArray;
Current result:
[
{
"customerID": 1,
"customerName": "Client1",
"data": {
"ordersAmount1": 10,
"earnings2": 882.53,
"date": "2019-02-21"
},
"ordersAmount1": 10,
"earnings2": 882.53
},
{
"customerID": 1,
"customerName": "Client1",
"data": {
"ordersAmount3": 18,
"earnings4": 21,
"date": "2019-02-20"
},
"ordersAmount3": 18,
"earnings4": 21
},
{
"customerID": 2,
"customerName": "Client2",
"data": {
"ordersAmount5": 43,
"earnings6": 11,
"date": "2019-02-21"
},
"ordersAmount5": 43,
"earnings6": 11
},
{
"customerID": 2,
"customerName": "Client2",
"data": {
"ordersAmount7": 21,
"earnings8": 41,
"date": "2019-02-20"
},
"ordersAmount7": 21,
"earnings8": 41
}
]
I would like to this result:
[
{
customerID: 1,
customerName: "Client1",
data: [
{
date: "2019-02-20"
},
{
date: "2019-02-21"
}
],
ordersAmount1: 12,
earnings2: 3333,
ordersAmount3: 1,
earnings4: 2,
},
{
customerID: 2,
customerName: "Client2",
data: [
{
date: "2019-02-20"
},
date: "2019-02-21"
}
],
ordersAmount1: 3112,
earnings2: 44,
ordersAmount3: 121,
earnings4: 2543,
}
],
Edit: this is my $data:
[
{
"date": "2019-02-21T00:00:00+01:00",
"ordersAmount": 10,
"earnings": 882.53,
"id": 1,
"customerName": "Client1"
},
{
"date": "2019-02-20T00:00:00+01:00",
"ordersAmount": 18,
"earnings": 21,
"id": 1,
"customerName": "Client1"
},
{
"date": "2019-02-21T00:00:00+01:00",
"ordersAmount": 43,
"earnings": 11,
"id": 2,
"customerName": "Client2"
},
{
"date": "2019-02-20T00:00:00+01:00",
"ordersAmount": 21,
"earnings": 41,
"id": 2,
"customerName": "Client2"
}
]
See below code, It'll Solve your issue
$data = $repository->findByData($fromDate, $toDate);
// define $resultIteration array
$resultIteration = [];
foreach ($data as $key => $d) {
$i = 1;
$date = $d['date'];
//$dateString = $date->format('Y-m-d');
$dateString = date('Y-m-d', strtotime($date));
$resultIteration[$key] = [
'customerID' => $d['id'],
'customerName' => $d['customerName'],
'data' => [
[
'date' => $dateString,
], [
'date' => $dateString
]
],
'ordersAmount' . $i++ => $d['ordersAmount'],
'earnings' . $i++ => $d['earnings'],
'ordersAmount' . $i++ => $d['ordersAmount'],
'earnings' . $i++ => $d['earnings'],
];
// REMOVE THIS LINE
//array_push($resultArray,$resultIteration[$key]);
}
// RETURN $resultIteration in response
return $resultIteration;
This should do it, using array_reduce and array_map:
$data_by_customer = array_reduce($data, function ($result, $entry) {
$result[$entry['id']][] = $entry;
return $result;
}, []);
$result = array_values(array_map(function ($customer_entries) {
$first_entry = reset($customer_entries);
$entry = [
'customerID' => $first_entry['id'],
'customerName' => $first_entry['customerName'],
'data' => []
];
$index = 0;
foreach ($customer_entries as $customer_entry) {
$entry += [
'ordersAmount' . ++$index => $customer_entry['ordersAmount'],
'earnings' . ++$index => $customer_entry['earnings'],
];
$entry['data'][] = ['date' => $customer_entry['date']->format('Y-m-d')];
}
return $entry;
}, $data_by_customer));
Demo: https://3v4l.org/BStNF

Sum values of same key that have same user id also retain there date as key

I am trying to add up the totalPlayed values for each user_id. I don't want to change the overall structure of the array, I just want to condense the events subarrays by summing the totalPlayed values.
Here is a sample array as json:
[
{
"date": "May 29",
"date_start": "2018-05-29 05:00:00",
"date_end": "2018-05-30 04:59:59",
"interval": 29,
"calDay": "29 days ago",
"events": [
{
"totalPlayed": 184,
"user_id": 479
},
{
"totalPlayed": 906,
"user_id": 479
},
{
"totalPlayed": 400,
"user_id": 1446
},
{
"totalPlayed": 500,
"user_id": 1446
}
]
},
{
"date": "May 30",
"date_start": "2018-05-30 05:00:00",
"date_end": "2018-05-31 04:59:59",
"interval": 28,
"calDay": "28 days ago",
"events": [
{
"totalPlayed": 1469,
"user_id": 1626
},
{
"totalPlayed": 1482,
"user_id": 1690
},
{
"totalPlayed": 1434,
"user_id": 1690
}
]
}
]
I would like this result:
array (
0 =>
array (
'date' => 'May 29',
'date_start' => '2018-05-29 05:00:00',
'date_end' => '2018-05-30 04:59:59',
'interval' => 29,
'calDay' => '29 days ago',
'events' =>
array (
0 =>
array (
'totalPlayed' => 1090,
'user_id' => 479,
),
1 =>
array (
'totalPlayed' => 900,
'user_id' => 1446,
),
),
),
1 =>
array (
'date' => 'May 30',
'date_start' => '2018-05-30 05:00:00',
'date_end' => '2018-05-31 04:59:59',
'interval' => 28,
'calDay' => '28 days ago',
'events' =>
array (
0 =>
array (
'totalPlayed' => 1469,
'user_id' => 1626,
),
1 =>
array (
'totalPlayed' => 2916,
'user_id' => 1690,
),
),
),
)
I tried the following function but it is not working for me.
function computeOutcome($array) {
$result=array();
foreach ($array as $item) {
$id=$item['user_id'];
if (isset($result[$id])) {
$result[$id]=$result[$id]+$item['time_played'];
} else {
$result[$id]=$item['time_played'];
}
}
return $result;
}
Using temporary keys on your deep subarrays offers improved performance versus iterated in_array() calls. See inline comments for more explanations.
Code: (Demo)
function computeOutcome($array) {
foreach ($array as &$set) { // modify by reference
$tmp = []; // use a fresh temporary array for each date-set
foreach ($set['events'] as $record) {
$id = $record['user_id'];
if (isset($tmp[$id])) {
$tmp[$id]['totalPlayed'] += $record['totalPlayed']; // increase the tally
} else {
$tmp[$id] = $record; // store the whole row
}
}
$set['events'] = array_values($tmp); // remove temporary keys
}
return $array;
}
$json = <<<JSON
[
{
"date": "May 29",
"date_start": "2018-05-29 05:00:00",
"date_end": "2018-05-30 04:59:59",
"interval": 29,
"calDay": "29 days ago",
"events": [
{
"totalPlayed": 184,
"user_id": 479
},
{
"totalPlayed": 906,
"user_id": 479
},
{
"totalPlayed": 400,
"user_id": 1446
},
{
"totalPlayed": 500,
"user_id": 1446
}
]
},
{
"date": "May 30",
"date_start": "2018-05-30 05:00:00",
"date_end": "2018-05-31 04:59:59",
"interval": 28,
"calDay": "28 days ago",
"events": [
{
"totalPlayed": 1469,
"user_id": 1626
},
{
"totalPlayed": 1482,
"user_id": 1690
},
{
"totalPlayed": 1434,
"user_id": 1690
}
]
}
]
JSON;
var_export(computeOutcome(json_decode($json, true)));
Assuming your main array is called $data, this solution builds a new array with summed values, you can then replace the original array with the new one created:
function computeOutcome($array) {
$events = [];
foreach ($array as $evt) {
if (in_array($evt['user_id'], array_column($events, 'user_id'))) {
$key = array_search($evt['user_id'], array_column($events, 'user_id'));
$events[$key]['totalPlayed'] += $evt['totalPlayed'];
} else {
$events[] = $evt;
}
}
return $events;
}
$data['events'] = computeOutcome($data['events']);
EDIT:
As you have a JSON string, I got the same result as before doing this (assuming your JSON string is called $json):
$data = json_decode($json, true);
// Still using the function I wrote before
for ($i = 0; $i < count($data); $i++) {
$data[$i]['events'] = computeOutcome($data[$i]['events']);
}
// Print result
echo '<pre>' . print_r($data, true) . '</pre>';
Documentation:
in_array()
array_column()
array_search()
You can use array_column to find the user id, array_intersect to find the respective values and array_sum to sum the values.
$data =json_decode('
[
{
"date": "May 29",
"date_start": "2018-05-29 05:00:00",
"date_end": "2018-05-30 04:59:59",
"interval": 29,
"calDay": "29 days ago",
"events": [
{
"totalPlayed": 184,
"user_id": 479
},
{
"totalPlayed": 906,
"user_id": 479
},
{
"totalPlayed": 400,
"user_id": 1446
},
{
"totalPlayed": 500,
"user_id": 1446
}
]
},
{
"date": "May 30",
"date_start": "2018-05-30 05:00:00",
"date_end": "2018-05-31 04:59:59",
"interval": 28,
"calDay": "28 days ago",
"events": [
{
"totalPlayed": 1469,
"user_id": 1626
},
{
"totalPlayed": 1482,
"user_id": 1690
},
{
"totalPlayed": 1434,
"user_id": 1690
}
]
}
]', true);
Foreach($data as $key => $arr){
$userid = array_column($arr["events"], "user_id");
$played = array_column($arr["events"], "totalPlayed");
Foreach(array_unique($userid) as $id){
$temp = array_intersect_key($played, array_intersect($userid, [$id]));
$new[$key]['events']['date'] = $arr['date'];
$new[$key]['events'][] = ['total played' => array_sum($temp), 'user_id' => $id];
}
}
Var_dump($new);
https://3v4l.org/G1jKI
This will only loop the unique userids instead of the full array.
In the future you can post arrays as Json or as var_export and it will be much easier for us to help you.

Laravel 5.5 API Resource Collection using array

I am using laravel 5.5 API resource collection for showing list of products. I am using an array instead of the modal object. I got my list of items but i am not sure how to show item variant array in the response. The actual response array passing to the api resource are:
{
"ID": 3160,
"UserID": 3302,
"ItemName": "2",
"Description": "2",
"Price": 2,
"StockLimited": true,
"StockQuantity": 19,
"CurrencyCode": "USD",
"ImageUrl": "/images/items/Item3302-636434761494584365-qq5HFm.png",
"VariantItems": [
{
"ID": 3181,
"ItemName": "2",
"Price": 0,
"StockLimited": false,
"StockQuantity": 0,
"CurrencyCode": "USD",
"Variants": [
{
"Id": 1099,
"Name": "Red",
"VariantGroupId": 1027,
"VariantGroupName": "Colour"
},
{
"Id": 1111,
"Name": "S",
"VariantGroupId": 1028,
"VariantGroupName": "Size"
}
]
},
{
"ID": 3182,
"ItemName": "2",
"Price": 0,
"StockLimited": false,
"StockQuantity": 0,
"CurrencyCode": "USD",
"Variants": [
{
"Id": 1099,
"Name": "Red",
"VariantGroupId": 1027,
"VariantGroupName": "Colour"
},
{
"Id": 1112,
"Name": "M",
"VariantGroupId": 1028,
"VariantGroupName": "Size"
}
]
}
],
"MerchantName": "seller",
"VariantGroupLists": [
{
"VariantGroupName": "Colour",
"Names": [
"Red",
"Grey",
"Navy"
]
},
{
"VariantGroupName": "Size",
"Names": [
"S",
"M",
"L",
"XL"
]
}
]
}
My ItemResourceCollection
public function toArray($request)
{
return [
'data' => $this->collection->map(function ($item) use ($request) {
return (new ItemResource($item))->toArray($request);
})
];
}
My ItemResource
public function toArray($request)
{
return [
"item_id" => $this->resource->ID,
"item_name" => $this->resource->ItemName,
"description" =>$this->resource->Description,
"item_price"=>$this->resource->Price,
"stock_qty"=>$this->resource->StockQuantity,
"currency_code"=>$this->resource->CurrencyCode,
"item_image_url"=>$this->resource->ImageUrl,
];
}
public function with($request)
{
return ['item_varients' => [new ItemResource($this->resource->VariantItems)]];
}
What I am trying to achieve is this result:
[
{
"item_id": 3160,
"item_name": "BLACK COWL NECK DRESS WITH DIAMANTE DETAIL",
"description": "Test test",
"item_price": 16.99,
"stock_qty": 20,
"currency_code": "SGD",
"item_image_url": "/images/items/Item18-636231488325192562-GoiIBl.png",
"item_varients": [
{
"id": 29,
"name": "Red",
"varientgroupId": 11,
"varientgroupname": "Color"
}
]
}
]
the with() method in ItemResource is not working. How do I add the "item_varients" array in the resource response?
I would first move the line to add the item_varients property inside the method toArray like following:
public function toArray($request)
{
return [
"item_id" => $this->resource->ID,
"item_name" => $this->resource->ItemName,
"description" => $this->resource->Description,
"item_price" => $this->resource->Price,
"stock_qty" => $this->resource->StockQuantity,
"currency_code" => $this->resource->CurrencyCode,
"item_image_url" => $this->resource->ImageUrl,
"item_varients" => [
new ItemResource($this->resource->VariantItems)
]
]
}
Meta information
We do that because the with is meant for to add a meta information block or something similar. See https://laravel.com/docs/5.5/eloquent-resources#adding-meta-data
The advantage when using the with method is, that multiple resources results in only one meta block. This is not what you want, I think.
Resource for VariantItem
I think you have to build a Resource class also for VariantItem. And because the VariantItems are a collection you also have to fill your array called item_variants similar to your ItemResourceCollection. So change your code to look like this:
public function toArray($request)
{
/**
* Prepare your variant items to have a collection
*
* #var Collection $variantItems
*/
$variantItems = collect($this->resource->VariantItems);
return [
"item_id" => $this->resource->ID,
"item_name" => $this->resource->ItemName,
"description" => $this->resource->Description,
"item_price" => $this->resource->Price,
"stock_qty" > $this->resource->StockQuantity,
"currency_code" => $this->resource->CurrencyCode,
"item_image_url" => $this->resource->ImageUrl,
"item_varients" => VariantItem::collection($variantItems)
]
}
Here is the best way to get specific fields using a collection
return [
'id' => $this->id,
'variant_item' => new CustomResource($this->user, ['item_id', 'item_name', 'item_size'])
];

Categories