How to collapse a collection into one and sum specific keys - php

I have following collection:
array:2 [▼
0 => array:21 [▼
"id" => 1
"referrer" => "Kariane Muller DVM"
"path" => null
"tracking_code_id" => 30
"sales" => 2
"field_name" => "ssn"
"income" => 4.0
"fsId" => "1b5e4434-da5a-342e-9b87-df9791b1bcc1"
]
2 => array:21 [▼
"id" => 1
"referrer" => "Kariane Muller DVM"
"path" => null
"tracking_code_id" => 30
"sales" => 1
"field_name" => "income"
"income" => 1.0
"fsId" => "1b5e4434-da5a-342e-9b87-df9791b1bcc1"
]
For visibility purposes I have converted it to array.
How can I collapse this collection into one collection, but I want to
sum the income keys into one in final collection.
Other keys should be included as they are ?

I would start with reduce. If I am understanding correctly you basically want the original collection intact but the income to be summed and reflected in every item.
$summedIncome = $collection->reduce(function ($carry, $item) {
$income = $carry->has('income') ? ($item->income + $carry->income) : $item->income;
$carry->push($item);
$carry->each->income = $income;
return $carry;
}, collect([]));

Related

Filter Array based on if contains php

My aim here is to remove the whole object from the array if "record_type" is null. I should then only be left with data that has a record_type set in the array.
I've looked into the array filter not sure how I target the data within the array "record_type". My only other thought is to import to a database and then SQL query what I want then delete data I've had which is much more overkill.
<pre>
array:36 [▼
0 => array:3 [▼
"type" => "comment"
"line_index" => 0
"text_b64" => "OyBjUGFuZWwgZmlyc3Q6ODguMC4xMiAodXBkYXRlX3RpbWUpOjE2MTg1NDYxNDIgQ3BhbmVsOjpab25lRmlsZTo6VkVSU0lPTjoxLjMgaG9zdG5hbWU6cjExOC5sb24yLm15c2VjdXJlY2xvdWRob3N0LmNvbSBs ▶"
]
1 => array:3 [▼
"line_index" => 1
"text_b64" => "OyBab25lIGZpbGUgZm9yIG9ha3RyZWVkZW50YWxtb3J0aW1lci5jby51aw=="
"type" => "comment"
]
2 => array:3 [▼
"text_b64" => "JFRUTCAxNDQwMA=="
"line_index" => 2
"type" => "control"
]
3 => array:6 [▼
"line_index" => 3
"dname_b64" => "b2FrdHJlZWRlbnRhbG1vcnRpbWVyLmNvLnVrLg=="
"record_type" => "SOA"
"ttl" => 30
"type" => "record"
"data_b64" => array:7 [▶]
]
4 => array:6 [▼
"dname_b64" => "b2FrdHJlZWRlbnRhbG1vcnRpbWVyLmNvLnVrLg=="
"line_index" => 10
"record_type" => "NS"
"ttl" => 30
"type" => "record"
"data_b64" => array:1 [▶]
]
5 => array:6 [▼
"ttl" => 30
"dname_b64" => "b2FrdHJlZWRlbnRhbG1vcnRpbWVyLmNvLnVrLg=="
"line_index" => 11
"record_type" => "NS"
"data_b64" => array:1 [▶]
"type" => "record"
]
</pre>
Goal: only be left with data that has a 'record_type' set (not null) in the array
Solution: use array_filter() on your source array ($arr) and filter for records with 'record_type' != "NS" (assuming "NS" is what you refer to as null, or not set).
<?php
$result = array_filter(
$arr,
function (array $record) {
if (isset($record['record_type'])) {
return $record['record_type'] != "NS";
} return null;
}
);
working demo
EDIT
If you want to filter for only those records that are of a certain type, e.g. type 'record', following should help:
<?php
$result = array_filter(
$arr,
function (array $record) {
if ($record['type'] == 'record') {
return true;
} return false;
}
);
working demo
If your Goal is to filter array to remove any inner-array that hasn't a record_type, So use array_filter with a callback function fn to check that record_type exist with isset function.
array_filter($arr, fn($el)=>isset($el["record_type"]));

Convert Collection to Array with I=ID in Laravel

I am beginner php developer.
I have small problem with my array. I use in my project Laravel 7
I have this code:
$items = collect($discount->products->toArray());
It's result me:
Illuminate\Support\Collection {#1783 ▼
#items: array:2 [▼
0 => array:17 [▼
"id" => 1
"product_category_id" => 5
"image_id" => null
"vat_type_id" => 1
"name" => "Produkt 1"
"slug" => "produkt-1"
"lead" => "<p>fewferfer</p>"
"description" => "<p> </p>"
"price" => "123.00"
"loyalty_program_points_price" => 2
"min_student_level" => null
"marked_as_available_at" => "2020-09-30T11:45:51.000000Z"
"deactivated_at" => null
"created_at" => "2020-09-30T11:45:23.000000Z"
"updated_at" => "2020-09-30T11:45:51.000000Z"
"deleted_at" => null
"pivot" => array:3 [▶]
]
1 => array:17 [▼
"id" => 2
"product_category_id" => 5
"image_id" => null
"vat_type_id" => 1
"name" => "Produkt 2"
"slug" => "produkt-2"
"lead" => "<p> </p>"
"description" => "<p>fergfer</p>"
"price" => "21.00"
"loyalty_program_points_price" => 3
"min_student_level" => null
"marked_as_available_at" => "2020-09-30T11:45:38.000000Z"
"deactivated_at" => null
"created_at" => "2020-09-30T11:45:38.000000Z"
"updated_at" => "2020-09-30T11:45:38.000000Z"
"deleted_at" => null
"pivot" => array:3 [▶]
]
]
}
I need convert it to this result:
Array{
1 => 1 (id)
2 => (id)
}
How can I make it?
I need array with only id values
Collections have the pluck method, which allows you to grab all the values of a specific key. To get all of the ids, you'd just need to do
$ids = $items->pluck('id')->all();
If you want them to have the same key as the id as well, pass that as the second parameter
$ids = $items->pluck('id', 'id')->all();
As a note, if products is a relation, then it's already a collection. You do not need to convert it to an array, then back to a collection:
$product_ids = $discount->products->pluck('id')->all();

Passing array of arrays to blade

I have a problem to passing array of arrays from controller to view in Laravel. I've done some research but none of topics helped. My tables are Shops, Items, Items Price. Shops contains shop id, which I get for use from url application/id. In Items Price I got information like shop_id , item_id (these two are FK), price. This table shows which items are in which shops. And in Items I have information about items: id ,picture. When I go to application/1, I want site to show items, which are in this specific shop, information.
My controller method:
public function getItems($id)
{
$items=ItemPrice::where('shop_id', $id)->select('item_id')->get()->toArray();
foreach($items as $item)
$products[] = array(Item::where('id',$item)->get()->toArray());
$shops=Shop::all();
return view('shop')->with(compact(['products','shops']));
}
when I debugging array with dd($products); I get:
array:4 [▼
0 => array:1 [▼
0 => array:1 [▼
0 => array:5 [▼
"id" => 1
"name" => "Item1"
"price" => 0.8
"type" => 2
"img_dir" => "svg/d.jpg"
]
]
]
1 => array:1 [▼
0 => array:1 [▼
0 => array:5 [▼
"id" => 2
"name" => "Item2"
"price" => 1.1
"type" => 2
"img_dir" => "svg/d2.jpg"
]
]
]
2 => array:1 [▼
0 => array:1 [▼
0 => array:5 [▼
"id" => 3
"name" => "Item3"
"price" => 3.1
"type" => 5
"img_dir" => "svg/p1.jpg"
]
]
]
3 => array:1 [▼
0 => array:1 [▼
0 => array:5 [▼
"id" => 4
"name" => "Item4"
"price" => 1.56
"type" => 5
"img_dir" => "svg/p2.jpg"
]
]
]
]
In view I do foreach #foreach($products as $product) and I get error:
Trying to get property 'img_dir' of non-object.
Any help would be appreciated.
Try like this
public function getItems($id)
{
$items = ItemPrice::where('shop_id', $id)
->select('item_id')
->pluck('item_id')
->toArray();
$products = Item::whereIn('id', $items)->get();
$shops = Shop::all();
return view('shop', compact('products','shops'));
}
You have some nested arrays in $products. What you think is a product is in fact an array. Maybe if you simplify your $products variable content :
public function getItems($id) {
$items = ItemPrice::where('shop_id', $id)->select('item_id')->get()->toArray();
$products = [];
foreach($items as $item) {
$products[] = Item::where('id',$item)->get();
}
$shops = Shop::all();
return view('shop')->with(compact(['products', 'shops']));
}
Look at this line
$products[] = array(Item::where('id',$item)->get()->toArray());
$products is an array, and you assign a new element which is an array made by the array value of your query (which contains array).
So you have a 3-level nested array which leads to confusion.
Why don't you just send to your blade view $products = Item::where('id',$item)->get(); ?

sending 2 values in 1 array in laravel

I have 2 arrays of data and i need to join the results of this two arrays.
I can use array_combine but there is 2 issues:
my first array is coming from check boxes and send data of checked check-box only
my second array coming from input fields and sending all my inputs
the issue here is my first array have (for example) 3 values while my second array has 7 values
This kind of combine will return the error of:
array_combine(): Both parameters should have an equal number of elements
Data
this is my dd results:
"optionID" => array:3 [▼ //check-box values
0 => "1"
1 => "2"
2 => "11"
]
"optionPRICE" => array:8 [▼ //input values
0 => "98"
1 => null
2 => null
3 => null
4 => null
5 => null
6 => null
7 => null
]
what I want
what i'm looking for is to get values of my inputs from checked check-boxes and not the rest of them.
example i check data 1,2,3 i get value 1,2,3 whether is null of filled and not getting 4,5,6,7 as you see in my dd data above.
code
this is what my html looks like
<tr>
<td class="text-center" width="50">
<label class="switch switch-small">
<input type="checkbox" name="optionID[]" value="11"> //checkbox arrays
<span></span>
</label>
</td>
<td>
CDN
</td>
<td class="text-center">
//input arrays
<input class="form-control" type="number" name="optionPRICE[]">
</td>
</tr>
I'm looking to get my data in any of this 2 kinds
this
"optionID" => array:3 [▼ //check-box values
0 => "1"
1 => "2"
2 => "11"
]
"optionPRICE" => array:8 [▼ //input values
0 => "98"
1 => null
2 => null
]
or this
"optionID" => array:3 [▼
"array" => array:2 [
0 => "1",
1 => "98",
],
"array" => array:2 [
0 => "2",
1 => null,
],
"array" => array:2[
0 => "11",
1 => null,
],
]
any idea?
Update
Based on answers if i use mapping function to return data as my second sample desire it would be confusing to naming data and relate them to my model column, so I share my model names and explain which data has to go in which column in order to be save in right place.
Schema
Schema::create('product_options', function (Blueprint $table) {
$table->increments('id');
$table->integer('product_id')->nullable()->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('option_id')->nullable()->unsigned();
$table->foreign('option_id')->references('id')->on('options')->onDelete('cascade');
$table->string('price')->nullable();
});
optionID data has to go in option_id column and optionPRICE data to price column.
For product_id column as this data will save after my product is saved so i just simply get the id by $product->id that column is out of issue.
Update 2
I tried many different conditions to see how data will send here is a serious issue based on the answers I've got.
When I pass data from 1 selected option, e.g. select colors and fill some data to send it works as it supposed to.
But when I select second option e.g. select sizes for this option only checkbox id's will send and data of optionPRICE will not send, this issue applies for next options as well (option 3,4,5,6....)
Here is a sample:
array:3 [▼
0 => array:3 [▼ //this is from colors (included price)
"option_id" => "4"
"price" => "768"
"product_id" => 50
]
1 => array:3 [▼ //this is also from colors (included price)
"option_id" => "5"
"price" => "5467"
"product_id" => 50
]
2 => array:3 [▼ //this one is from sizes (not included price, while i filled the price!)
"option_id" => "10"
"price" => null
"product_id" => 50
]
]
Update 3
OK, I worked around and tested some ways to fix the issue, here is what i figured and what i achieved:
Th issue of returning my third price null (update 2) was because we were counting prices array and not inputs of checked check-boxes. that was why 3rd option was null while my actual filled option was 11. (refer to my data dd top of my question)
I can get now actual filled optionPRICE fields and not by counting them but with the values.
Here is what I did so far:
$options = $request->input('optionID');
$prices = $request->input('optionPRICE');
$collection = array_filter($prices, function($value) {
return !is_null($value);
});
$toInsert = array_merge([$options, $collection]);
dd($toInsert);
code above returns this:
array:2 [▼
0 => array:3 [▼
0 => "4"
1 => "5"
2 => "10"
]
1 => array:3 [▼
0 => "768"
1 => "5467"
5 => "5465496"
]
]
but if i fill price for only 2 of my check-boxes will be like:
array:2 [▼
0 => array:3 [▼
0 => "4"
1 => "5"
2 => "10"
]
1 => array:2 [▼
0 => "768"
1 => "5467"
]
]
This is good to get only input data without needs of counting array elements just pure values.
After this I need to join this 2 array of mine and return null in case of second case happens (3 check-box, 2 price).
Any help on that?
This should get you your first desired solution using array_slice:
// Get the length of the smaller array
$minLength = min(count($arr1), count($arr2));
$result = [
'optionID' => array_slice($arr1, 0, $minLength),
'optionPRICE' => array_slice($arr2, 0, $minLength),
];
For your second desired solution, you could try something like this (if I understood correctly):
$checkboxes = [1, 2, 11];
$inputs = [98, null, null];
$result = collect($checkboxes)->map(function ($item, $key) use($inputs) {
return [$item, $inputs[$key] ?? null];
})->toArray();
dd($result);
// array:3 [
// 0 => array:2 [
// 0 => 1
// 1 => 98
// ]
// 1 => array:2 [
// 0 => 2
// 1 => null
// ]
// 2 => array:2 [
// 0 => 11
// 1 => null
// ]
// ]

Push a set of values to an array based on another unique value in the same array

Question background
Hello, I have the following array of movie crew members:
array:7 [▼
0 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5e9d"
"department" => "Directing"
"id" => 139098
"job" => "Director"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
1 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5ed7"
"department" => "Writing"
"id" => 139098
"job" => "Story"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
2 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5edd"
"department" => "Writing"
"id" => 132973
"job" => "Story"
"name" => "Ben Coccio"
"profile_path" => null
]
3 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5ee3"
"department" => "Writing"
"id" => 139098
"job" => "Screenplay"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
4 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5ee9"
"department" => "Writing"
"id" => 132973
"job" => "Screenplay"
"name" => "Ben Coccio"
"profile_path" => null
]
5 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5eef"
"department" => "Writing"
"id" => 1076793
"job" => "Screenplay"
"name" => "Darius Marder"
"profile_path" => null
]
11 => array:6 [▼
"credit_id" => "52fe49de9251416c750d5f13"
"department" => "Camera"
"id" => 54926
"job" => "Director of Photography"
"name" => "Sean Bobbitt"
"profile_path" => null
]
]
As you can see this is a list of credits I'm getting via the TMDb API. The first step of building the above array was to filter out all jobs that I don't want to display, here's how I did that:
$jobs = [ 'Director', 'Director of Photography', 'Cinematography', 'Cinematographer', 'Story', 'Short Story', 'Screenplay', 'Writer' ];
$crew = array_filter($tmdbApi, function ($crew) use ($jobs) {
return array_intersect($jobs, $crew);
});
My question
I'd like to figure out how to take the above result one step further and combine jobs where the id is the same, so as to end up with something like this, for example:
array:7 [▼
0 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5e9d"
"department" => "Directing"
"id" => 139098
"job" => "Director, Story, Screenplay"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
I have also considered ditching doing this in my logic and instead doing it in my blade template, but I'm not sure how to achieve that.
How would you accomplish this?
You could nicely use Laravel's Collection in such a situation, which has a great number of methods which will help you in this case.
First, turn this array (the one you already filtered on jobs) to a Collection:
$collection = collect($crew);
Second, group this Collection by it's ids:
$collectionById = $collection->groupBy('id');
Now, the results are grouped by the id and transformed to a Collection in which the keys correspond to the id, and the value an array of 'matching' results. More info about it here.
Finally, just a easy script that iterates through all the results for each id and combines the job field:
$combinedJobCollection = $collectionById->map(function($item) {
// get the default object, in which all fields match
// all the other fields with same ID, except for 'job'
$transformedItem = $item->first();
// set the 'job' field according all the (unique) job
// values of this item, and implode with ', '
$transformedItem['job'] = $item->unique('job')->implode('job', ', ');
/* or, keep the jobs as an array, so blade can figure out how to output these
$transformedItem['job'] = $item->unique('job')->pluck('job');
*/
return $transformedItem;
})->values();
// values() makes sure keys are reordered (as groupBy sets the id
// as the key)
At this point, this Collection is returned:
Collection {#151 ▼
#items: array:4 [▼
0 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5e9d"
"department" => "Directing"
"id" => 139098
"job" => "Director, Story, Screenplay"
"name" => "Derek Cianfrance"
"profile_path" => "/zGhozVaRDCU5Tpu026X0al2lQN3.jpg"
]
1 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5edd"
"department" => "Writing"
"id" => 132973
"job" => "Story, Screenplay"
"name" => "Ben Coccio"
"profile_path" => null
]
2 => array:6 [▼
"credit_id" => "52fe49dd9251416c750d5eef"
"department" => "Writing"
"id" => 1076793
"job" => "Screenplay"
"name" => "Darius Marder"
"profile_path" => null
]
3 => array:6 [▼
"credit_id" => "52fe49de9251416c750d5f13"
"department" => "Camera"
"id" => 54926
"job" => "Director of Photography"
"name" => "Sean Bobbitt"
"profile_path" => null
]
]
}
Note: to use this Collection as an array, use:
$crew = $combinedJobCollection->toArray();
There are multiple ways to achieve this, for example: search the array for overlapping id's, but I think this is the easiest way to achieve this.
Goodluck!
Since you are trying to edit the array elements and its size, I believe array_map() or array_filter() won't be a solution to this.
This is what I could come up with...
$jobs = [
'Director', 'Director of Photography', 'Cinematography',
'Cinematographer', 'Story', 'Short Story', 'Screenplay', 'Writer'
];
$crew = [];
foreach($tmdbApi as $key => $member) {
if($member['id'] == $id && in_array($member['job'], $jobs)) {
if(!isset($crew[$key])) {
$crew[$key] = $member;
} else {
$crew_jobs = explode(', ', $crew[$key]['job']);
if(!in_array($member['job'], $crew_jobs)) {
$crew_jobs[] = $member['job'];
}
$crew[$key]['job'] = implode(', ', $crew_jobs);
}
}
}
Hope this answers your question :)

Categories