How to get value from a collection base on key name condition? - php

I am trying to get the value from a collection in PHP.
$todaylog variable contains a collection from a laravel query builder:
$todaylog = [
{
"row_id":55,
"emp_number":"IPPH0004",
"timestamp":"03:30:23",
"attendance_status":"Punch In",
"date_created":"2021-10-01"
},
{
"row_id":56,
"emp_number":"IPPH0004",
"timestamp":"11:32:50",
"attendance_status":"Start Break",
"date_created":"2021-10-01"
},
{
"row_id":57,
"emp_number":"IPPH0004",
"timestamp":"11:33:09",
"attendance_status":"End Break",
"date_created":"2021-10-01"
}
]
What I have done so far: but this approach is so slow:
$timein = DB::table('attendance')->select('timestamp')->where('attendance_status','Punch In')->get();
Now I want to have something like this: (PS this is only a pseudo code)
$timein = (where) $todaylog.attendance_status = "Punch In"
$endbreak = (where) $todaylog.attendance_status = "End Break"
Is this possible? or I have to query them to the database individually? Thanks

What you need in Laravel is something like that:
$rows = collect([
[
"row_id" => 55,
"emp_number" => "IPPH0004",
"timestamp" => "03:30:23",
"attendance_status" => "Punch In",
"date_created" => "2021-10-01"
],
[
"row_id" => 56,
"emp_number" => "IPPH0004",
"timestamp" => "11:32:50",
"attendance_status" => "Start Break",
"date_created" => "2021-10-01"
],
[
"row_id" => 57,
"emp_number" => "IPPH0004",
"timestamp" => "11:33:09",
"attendance_status" => "End Break",
"date_created" => "2021-10-01"
]]);
dd($rows->where('row_id', 57));
Result:
Illuminate\Support\Collection {#446 ▼
#items: array:1 [▼
2 => array:5 [▼
"row_id" => 57
"emp_number" => "IPPH0004"
"timestamp" => "11:33:09"
"attendance_status" => "End Break"
"date_created" => "2021-10-01"
]
]
}

you can use laravel collection where method:
collect($todaylog)->where("attendance_status", "Punch In")->first();

Related

Merge arrays in laravel including keys

I have 2 arrays and I want to merge them. (I can merge them) but I also need to include their unique keys in merged results and that part I cannot achieve.
sample
$prices = [
['112802' => "500000"],
['113041' => "1000000"],
];
$notes = [
['112802' => "note 2"],
['113041' => "note 1"],
];
$collection = collect($prices);
$zipped = $collection->zip($notes);
$zipped->toArray();
Unique keys are 112802 and 113041.
When I merge my array all I get is this:
[
[
"1000000",
"note 1"
],
[
"500000",
"note 2"
]
]
What I'm looking for is like this:
[
[
"id" => "112802",
"price" => "500000",
"note" => "note 2",
],
[
"id" => "113041",
"price" => "1000000",
"note" => "note 1",
]
}]
any suggestion?
This does what you want with the data you provide.
NOTE it will only work if your 2 arrays are the same size and the the keys are in the same order.
If this data comes from a database, it is likely it could have been produced in the format you actually wanted rather than having to fiddle with the data post fetch.
$prices = [
['112802' => "500000"],
['113041' => "1000000"],
];
$notes = [
['112802' => "note 2"],
['113041' => "note 1"],
];
$new = [];
foreach ($prices as $i=>$pr){
$k = key($pr);
$new[] = [ 'id' => $k,
'price' => $pr[$k],
'note' => $notes[$i][$k] ];
}
print_r($new);
RESULT
Array
(
[0] => Array (
[id] => 112802
[price] => 500000
[note] => note 2
)
[1] => Array (
[id] => 113041
[price] => 1000000
[note] => note 1
)
)
Here's another solution using some of Laravel's Collection methods.
It's not the most elegant, but it can be a starting point for you.
$prices = collect([
['112802' => "500000"],
['113041' => "1000000"],
])->mapWithKeys(function($item) {
// This assumes that the key will always be the ID and the first element is the price.
// Everythng else for each element will be ignored.
$id = array_keys($item)[0];
return [$id => ["id" => $id, "price" => reset($item)]];
});
$notes = collect([
['112802' => "note 2"],
['113041' => "note 1"],
])->mapWithKeys(function($item) {
$id = array_keys($item)[0];
return [$id => ["id" => $id, "note" => reset($item)]];
});
$result = $prices->zip($notes)->map(function ($item) {
// Feel free to call `toArray()` here if you don't want a Collection.
return collect($item)->mapWithKeys(function ($a) { return $a; });
});
Below is the $result (called using dd()).
Illuminate\Support\Collection {#1886 ▼
#items: array:2 [▼
0 => Illuminate\Support\Collection {#1888 ▼
#items: array:3 [▼
"id" => 112802
"price" => "500000"
"note" => "note 2"
]
}
1 => Illuminate\Support\Collection {#1889 ▼
#items: array:3 [▼
"id" => 113041
"price" => "1000000"
"note" => "note 1"
]
}
]
}
It's achieved by extracting the ID so that the zip can join there, but then we need a little hack with the map and mapWithKeys in the $result.
That's just because otherwise each element in $result will still have two separate arrays for $prices and $notes.

Laravel - restructuring array for easy sync of many-to-many with additional pivot data

I have created what feels like a clunky solution to restructuring a data array in order to pass it to a sync() to update a many-to-many relationship with additional data in the pivot table and wondered if anyone could suggest a simpler approach.
I have an array coming from a request, here's a relevant extract:
"papers" => [
0 => [
"id" => 2
"code" => "123-321-888"
"name" => "Pop out"
"pivot" => [
"job_id" => 46
"paper_id" => 2
"qty_required" => 500
]
]
1 => [
"id" => 1
"code" => "444-666-999"
"name" => "Premium pro"
"pivot" => [
"job_id" => 46
"paper_id" => 1
"qty_required" => 1000
]
]
]
In order to do an easy sync of a many-to-many relationship with extra pivot data one needs to restructure that to:
[
paper[id] => [
'qty_required' => paper[pivot][qty_required]
]
]
Which for the above example would be:
[
2 => [
"qty_required" => "500"
]
1 => [
"qty_required" => "1000"
]
]
I'm currently doing a 2-step process to achieve this as follows:
$paperUpdate = Arr::pluck($request->input('papers'), 'pivot.qty_required', 'id');
//output: [ 2 => 500, 1 => 1000]
foreach ($paperUpdate as $key => $value) {
$paperSync[$key]['qty_required'] = $value;
}
//output: [ 2 => [ "qty_required" => "500" ], 1 => [ "qty_required" => "1000" ]
$job->papers()->sync($paperSync);
Is there an easier approach?
Your approach seems fine to me. If you want to nit pick, you could do one less iteration using:
$sync = array_reduce($request->input('papers'), function ($sync, $paper) {
$id = $paper['id'];
$sync[$id] = [ 'qty_required' => $paper['pivot']['qty_required'] ];
return $sync;
}, []);

Find the key and return true in a deeply nested set of arrays php

So there are tons of questions like this, but I haven't found an answer the suits my desired outcome.
consider the following array:
array:13 [
"Peripheral Neuropathy" => array:3 [
"name" => "peripheral_neuropathy"
"value" => array:1 [
0 => "yes"
]
"dependencies" => array:5 [
"neuropathy_staging_fap" => array:1 [
0 => "2"
]
"neuropathy_staging_pnd" => array:1 [
0 => "II"
]
"specify_type" => array:1 [
0 => "Sensory"
]
"autonomic" => array:1 [
0 => "yes"
]
"sensory_fiber_size" => array:1 [
0 => "Small"
]
]
]
"Neuropathic pain" => array:3 [
"name" => "neuropathic_pain"
"value" => array:1 [
0 => "yes"
]
"dependencies" => []
]
"Functional Motor Assessment" => array:3 [
"name" => "functional_motor_assessment"
"value" => array:1 [
0 => "Yes"
]
"dependencies" => array:3 [
"functional_motor_assessment_test_name" => "sample"
"functional_motor_assessment_test_score" => "10"
"functional_motor_assessment_date" => "2020-02-11"
]
]
"Assessment name" => array:3 [
"name" => "functional_motor_assessment_test_name"
"value" => "sample"
"dependencies" => []
]
"Assessment Score" => array:3 [
"name" => "functional_motor_assessment_test_score"
"value" => "10"
"dependencies" => []
]
"Assessment Date" => array:3 [
"name" => "functional_motor_assessment_date"
"value" => "2020-02-11"
"dependencies" => []
]
"Carpal Tunnel Syndrome" => array:3 [
"name" => "carpal_tunnel_syndrome"
"value" => array:1 [
0 => "yes"
]
"dependencies" => []
]
"EMG" => array:3 [
"name" => "emg"
"value" => array:1 [
0 => "yes"
]
"dependencies" => array:5 [
"emg_type" => array:1 [
0 => "Median"
]
"median_amplitude" => "10"
"median_CV" => "10"
"median_tml" => "10"
"median_size" => array:1 [
0 => "7cm"
]
]
]
"EMG Type" => array:3 [
"name" => "emg_type"
"value" => array:1 [
0 => "Median"
]
"dependencies" => array:4 [
"median_amplitude" => "10"
"median_CV" => "10"
"median_tml" => "10"
"median_size" => array:1 [
0 => "7cm"
]
]
]
"Amplitude" => array:3 [
"name" => "median_amplitude"
"value" => "10"
"dependencies" => []
]
"CV" => array:3 [
"name" => "median_CV"
"value" => "10"
"dependencies" => []
]
"TML" => array:3 [
"name" => "median_tml"
"value" => "10"
"dependencies" => []
]
"Size" => array:3 [
"name" => "median_size"
"value" => array:1 [
0 => "7cm"
]
"dependencies" => []
]
]
As we can see there are duplicates, for example look at: Functional Motor Assessment under dependencies - this is correct, but directly under Functional Motor Assessment is Assessment name with the name functional_motor_assessment_test_name.
This is the duplicate. This specific array, Assessment name should not exist because the name already exists in Functional Motor Assessment's dependencies array.
So I thought, I will write the following function:
protected function alreadyExists(array $values, string $fieldName) {
if (empty($values)) {
return false;
}
foreach ($values as $key => $value) {
foreach ($value as $k => $v) {
if ($k === 'dependencies' && !empty($value[$k])) {
return array_key_exists($fieldName, $value[$k]);
}
}
}
return false;
}
where $value is the above array and in this case $fieldName would be (for example) functional_motor_assessment_test_name.
The idea here is that this should walk through the array looking for any key that matches: functional_motor_assessment_test_name and return true if found or false if not (false if the (above) array is empty).
Theres a couple rules:
Return false if the value array is empty, because obviously it wont exist.
Return false if never found
Skip the check if the the dependencies array is empty (it can be empty sometimes).
This is where it should move on to the next array, so if not found in Peripheral Neuropathy move on to Neuropathic pain and so on ...
I think this function has to be recursive, but I am not sure where to put the recursive aspect, to say: well I didn't find it in Peripheral Neuropathy, let's check Neuropathic pain and so on and so forth. Until it is either found or not.
I tried array_walk_recursive, but as I expected while reading the docs, there is no way to break from that kind of function - so I thought, this function I have is on the right track, I just need to make it recursive.
Ideas?
There's no need to loop over the second level arrays, just get the dependencies element directly with indexing.
public function alreadyFound(array $values, string $fieldName) {
foreach ($values as $item) {
if (!empty($item['dependencies']) && array_key_exists($fieldName, $item['dependencies'])) {
return true;
}
return false;
}
If there can be dependencies nested within dependencies, you do need a recursive solution.
protected function alreadyExists(array $values, string $fieldName) {
if (array_key_exists($fieldName, $values)) {
return true;
}
foreach ($values as $item) {
if (!empty($item['dependencies']) && $this->alreadyExists($item['dependencies'], $fieldName) {
return true;
}
}
return false;
}

Complex Laravel collection

I'm having some issues with the Laravel Collection class.
What I'm trying to do:
I have a multisite solution in which a site has "facilitators". Sometimes one facilitator appears on multiple sites, and not just one.
I want to list all the facilitators and the website they're on the main page, but I don't want multiple users.
So what I currently do is:
Get the Facilitators.
Use Collection to collect the facilitators and use unique('name').
This gives me unique facilitators, but only picks the first one it detects and then deletes the other ones.
So lets say I have this collection:
Collection {
#items: array:3 [
0 => array:2 [
"name" => "John"
"site" => "Example"
]
1 => array:2 [
"name" => "Martin"
"site" => "Another"
]
2 => array:2 [
"name" => "John"
"site" => "Another"
]
]
}
With unique() I would get:
Collection {
#items: array:3 [
0 => array:2 [
"name" => "John"
"site" => "Example"
]
1 => array:2 [
"name" => "Martin"
"site" => "Another"
]
]
}
And this is what I want to get:
Collection {
#items: array:3 [
0 => array:2 [
"name" => "John"
"site" => ["Example", "Another"]
]
1 => array:2 [
"name" => "Martin"
"site" => "Another"
]
]
}
Does anyone have an idea how I could accomplish this with Laravel's collection class?
When stuck with collections always remember reduce is a powerful tool in your arsenal.
Building on Sam's answer which I couldn't get to work, I think using reduce alongside groupBy should work...
$sites = collect([
["name" => "John", "site" => "Example"],
["name" => "Martin", "site" => "Another"],
["name" => "John", "site" => "Another"],
]);
$sites->groupBy('name')->reduce(function ($result, $item) {
$result[] = [
'name' => $item->first()['name'],
'sites' => $item->pluck('site')->toArray()
];
return $result;
}, collect([]))->toArray();
And from the console...
λ php artisan tinker
Psy Shell v0.8.2 (PHP 7.0.10 ÔÇö cli) by Justin Hileman
>>> $sites = collect([
... ["name" => "John", "site" => "Example"],
... ["name" => "Martin", "site" => "Another"],
... ["name" => "John", "site" => "Another"],
... ]);
=> Illuminate\Support\Collection {#698
all: [
[
"name" => "John",
"site" => "Example",
],
[
"name" => "Martin",
"site" => "Another",
],
[
"name" => "John",
"site" => "Another",
],
],
}
>>> $sites->groupBy('name')->reduce(function ($result, $item) {
... $result[] = ['name' => $item->first()['name'], 'sites' => $item->pluck('site')->toArray()];
...
... return $result;
... }, collect([]))->toArray();
=> [
[
"name" => "John",
"sites" => [
"Example",
"Another",
],
],
[
"name" => "Martin",
"sites" => [
"Another",
],
],
]
One thing to note is that you specified in your question that the sites should return a single string if there's only one site and an array if there's many.The above solution does not provide this! I think this is inconsistent and you should always return an array for the sites key, even if it only has one value as it will make it more difficult to read and manipulate later on.
However, if this is something important, you could instead check if there are many sites when using pluck to set an array and if not you could set it as a single string, like this:
$sites->groupBy('name')->reduce(function ($result, $item) {
$result[] = [
'name' => $item->first()['name'],
'sites' => $item->pluck('site')->count() > 1 ? $item->pluck('site') : $item->first()['site']
];
return $result;
}, collect([]))->toArray();
which would produce...
[
[
"name" => "John",
"sites" => [
"Example",
"Another",
],
],
[
"name" => "Martin",
"sites" => "Another",
],
]
you can do it by chaining to get exactly what you want, assuming $collection is the main collection
$collection->groupBy('name')->map(function($facilitators) {
return ['name' => $facilitators->first()['name'], 'site' => $facilitators->pluck('site')->toArray()];
})->values()->toArray();
first we group by name so it will give 2 dimensional array inside collection, then iterate to that and name will be common so get it from first element, then from all the element pluck site and convert it to array, using flatMap will make it single level nested.

How to Count the number of objects in elequont Object inside a group by in a already queried collection?

This is my current output
Collection {#794 ▼
#items: array:8 [▼
"IN" => Collection {#795 ▶}
"NZ" => Collection {#787 ▶}
]}
I want the items to be hold the no of count for each codes like
"IN" => 4,
"NZ" => 3,
I know that I can directly write in a query like this
$query->groupBy('country_code')->orderBy('country_code', 'ASC');
return $query->get([
DB::raw('country_code as country_code'),
DB::raw('COUNT(*) as "count"')
]);
But I want the output from a already queried collection to reduce multiple queries which is a collection.
Right now I am only able to group by on the collection like this
$collection->groupBy('country_code');
$b = $a->groupBy('country_code');
You've done most of the job by proper grouping the data based on the country_code. Now it's just to iterate through the collection with a foreach, key, value and use the collections's count() method to count the number of elements stored under a given country_code
foreach ($b as $countryCode => $items) {
echo $items->count()."\n";
}
Reproduce:
php artisan ti
Psy Shell v0.7.2 (PHP 7.0.8-0ubuntu0.16.04.3 — cli) by Justin Hileman
>>> $cities = collect([['country_code' => 'pl', 'name' => 'Warszawa'], ['country_code' => 'pl', 'name' => 'Wrocław'], ['country_code' => 'de', 'name' => 'Berlin']]);
=> Illuminate\Support\Collection {#846
all: [
[
"country_code" => "pl",
"name" => "Warszawa",
],
[
"country_code" => "pl",
"name" => "Wrocław",
],
[
"country_code" => "de",
"name" => "Berlin",
],
],
}
>>> $grouped = $cities->groupBy('country_code');
=> Illuminate\Support\Collection {#836
all: [
"pl" => Illuminate\Support\Collection {#838
all: [
[
"country_code" => "pl",
"name" => "Warszawa",
],
[
"country_code" => "pl",
"name" => "Wrocław",
],
],
},
"de" => Illuminate\Support\Collection {#837
all: [
[
"country_code" => "de",
"name" => "Berlin",
],
],
},
],
}
>>> foreach ($grouped as $cCode => $cities) {
... echo $cCode . ' has '.$cities->count()."\n";
... }
pl has 2
de has 1

Categories