Laravel collection by month - php

I've got a collection and I need the total users by month.
So I've done this:
array_replace(array_fill_keys(range(0, 11), 0), $users->groupBy('created_at.month')->toArray())
It's sort of working because I get this:
array:12 [▼
0 => 0
1 => 0
2 => 0
3 => 0
4 => 0
5 => 0
6 => array:1 [▶]
7 => array:2 [▶]
8 => 0
9 => 0
10 => 0
11 => 0
]
The problem I face now is that I don't need the 2 arrays at position 6 and 7 but I need the counts.
So I was thinking of something like:
$users->groupBy('created_at.month')->count()->toArray();
But, that's obviously not working. Any ideas ?

try using map method
$collection->map(function ($item, $key) {
return count($item);
});

Related

Compare two arrays and if one month (key) is missing fill with 0

Hello I've got two arrays, one which is made with Carbon that takes the last 12 months from today (in Y-m format) and a Collection (which have an array of items that return every transaction of the last 12 months in the same Y-m format). What I want to do is fill in this items array if there's no transaction for example on the month 12, 9, 8 fill them with 0.
Here's the 2 arrays I need compared and fill missing Year-month with 0 (the last array is the balance which will be merged into the array for every month).
^ array:12 [▼
"2022-02" => 0
"2022-03" => 0
"2022-04" => 0
"2022-05" => 0
"2022-06" => 0
"2022-07" => 0
"2022-08" => 0
"2022-09" => 0
"2022-10" => 0
"2022-11" => 0
"2022-12" => 0
"2023-01" => 0
]
^ array:12 [▼
"2022-01" => 0
"2022-02" => 1
"2022-03" => 2
"2022-04" => 3
"2022-06" => 4
"2022-07" => 5
"2022-08" => 6
"2022-09" => 7
"2022-10" => 8
"2022-11" => 9
"2022-12" => 10
"2023-01" => 11
]
^ array:12 [▼
0 => 340
1 => 480
2 => 550
3 => 690
4 => 830
5 => 970
6 => 1110
7 => 1250
8 => 1460
9 => 1600
10 => 1670
11 => 1880
]
The code I used at the moment to find the balance per month (the latest balance of the month) but at the moment I dont print the missing months (it just skips them instead of filling with 0):
$period = CarbonPeriod::create(now()->subMonths(11), now())->month();
$dates = array();
$days = array();
foreach($period as $date) {
$dates[] = $date->format("Y-m");
$days[] = $date->lastOfMonth()->format("d-m-Y");
}
$userTransactions = auth()->user()->transactions;
$flipped = array_flip($dates);
$transactionsByMonth = $userTransactions->groupBy(function($d) {
$monthlyTransaction = Carbon::parse($d->created_at)->format('Y-m');
return $monthlyTransaction;
});
foreach($flipped as $key => $yearMonth){
$yearMonth = 0;
$flipped[$key] = $yearMonth;
}
dump($flipped);
foreach($transactionsByMonth as $transaction) {
if (sizeof($transaction) > 1){
$duplicatedTransactions = $transaction->groupBy(function($d) {
return Carbon::parse($d->created_at)->format('Y-m-d');
});
$lastDuplicatedTransactions = $duplicatedTransactions->last();
foreach($lastDuplicatedTransactions as $lastTransaction){
$transactionDates[] = $lastTransaction->created_at;
$transactionBalance[] = $lastTransaction->main_balance;
}
} else {
foreach($transaction as $notDuplicatedTransaction){
$transactionDates[] = $notDuplicatedTransaction->created_at;
$transactionBalance[] = $notDuplicatedTransaction->main_balance;
}
}
};
$transactionsPerMonth = [];
foreach($transactionDates as $date){
$date = Carbon::parse($date)->format('Y-m');
$transactionsPerMonth[] = $date;
}
$transactionsPerMonth = array_flip($transactionsPerMonth);
dump($transactionsPerMonth);
dump($transactionBalance);
At the moment I achieved printing the balance of the oldest day of the month on the last 12 months, what Im missing is comparing if of this 12 months if there's a month missing fill it with 0 instead of skipping it.
You can use array_key_exists() function to compare the two arrays and fill in missing months with 0.
Example:
$carbonMonths = ["2022-01", "2022-02", "2022-03", "2022-04", "2022-05", "2022-06", "2022-07", "2022-08", "2022-09", "2022-10", "2022-11", "2022-12"];
$transactions = [
["2022-01" => 200],
["2022-02" => 150],
["2022-03" => 100],
["2022-05" => 50]
];
foreach ($carbonMonths as $month) {
if (!array_key_exists($month, $transactions)) {
$transactions[$month] = 0;
}
}
In this example, the $carbonMonths array contains the last 12 months in Y-m format. The $transactions array contains transactions in the same format.
You can also use the array_merge() function to merge the two arrays and fill in any missing values with 0.
$merged = array_merge(array_fill_keys($carbonMonths, 0), $transactions);
Here $transactions should be an associative array with keys as month and value for this to work.
Both of the approach should give you the same result, you may use the one which you find easy to implement.
Improved the code to this:
$months = CarbonPeriod::create(now()->subMonths(11), now())->month();
$transactions = auth()->user()->transactions
->groupBy( fn($d) => Carbon::parse( $d->created_at )->format('Y-m'))->map->last();
$transactionsByMonth = collect($months)
->flatMap(function ($key) use ($transactions) {
$key = $key->format('Y-m');
return [$key => collect($transactions[$key]->main_balance ?? 0)];
});
$transactionsByMonth = $transactionsByMonth->toArray();
$transactionsByMonth = array_values($transactionsByMonth);
dump($transactionsByMonth);
But Im getting an array inside an array:
array:12 [▼
0 => array:1 [▼
0 => 480
]
1 => array:1 [▼
0 => 550
]
2 => array:1 [▼
0 => 690
]
3 => array:1 [▶]
4 => array:1 [▶]
5 => array:1 [▶]
6 => array:1 [▶]
7 => array:1 [▶]
8 => array:1 [▶]
9 => array:1 [▶]
10 => array:1 [▶]
11 => array:1 [▶]
]
So What I finally did to solve it was instead of returning the collect() for every $key I just returned the collect directly so I would stop getting an array inside an array.
Replaced this:
$transactionsByMonth = collect($months)
->flatMap(function ($key) use ($transactions) {
$key = $key->format('Y-m');
return [$key => collect($transactions[$key]->main_balance ?? 0)];
});
$transactionsByMonth = $transactionsByMonth->toArray();
$transactionsByMonth = array_values($transactionsByMonth);
dump($transactionsByMonth);
Into this:
$transactionsByMonth = collect($months)
->flatMap(function ($key) use ($transactions) {
$key = $key->format('Y-m');
return collect($transactions[$key]->main_balance ?? 0);
});
$transactionsByMonth = $transactionsByMonth->toArray();

How to get values from an array in Laravel

I have a 3 level category. I need to gain businesses on level 3 category.
I write this code:
$main_cat = Category::where(['slug' => $url])->first();
$lev_cat2 = Category::where(['parent_id' => $main_cat->id, 'status' => '1'])->get();
foreach ($lev_cat2 as $subCategory) {
$cat_ids_lv2[] = $subCategory->id . ',';
}
foreach ($lev_cat2 as $subCat) {
$lev_cat3[] = Category::where(['parent_id' => $subCat->id, 'status' => '1'])->pluck('id')->toArray();
}
dd($lev_cat3);
Then I got this array which is correct:
array:5 [▼
0 => array:3 [▼
0 => 145
1 => 146
2 => 147
]
1 => array:3 [▼
0 => 148
1 => 149
2 => 150
]
2 => array:3 [▼
0 => 151
1 => 152
2 => 153
]
3 => array:3 [▼
0 => 154
1 => 155
2 => 156
]
4 => []
]
now I dont know how can I get values like 145,146,147,148,149,... to pass theme to
Business::where(['category_id'=> [145,146,147,148,...]]->get();
of course dynamic.
You can use laravel collection helpers :
Business::whereIn('category_id', collect($lev_cat3)->flatten()->all())->get();
Since PHP 5.5.0 there is a built-in function array_column which does exactly this.
You can use it to Converting php array of arrays into single array then use it like this
$category_ids = array_column($lev_cat3);
Business::where(['category_id'=> $category_ids]]->get();

Calculate Age for each user from array of date using Laravel

i'm new to laravel i want to know if there are methode to calculate age from array so my content looks like this:
Collection {#231 ▼
#items: array:6 [▼
0 => "1928-11-18"
1 => "1938-06-15"
2 => "1939-03-30"
3 => "1941-11-08"
4 => "1940-04-29"
5 => "1987-06-24"
]
}
How to properly transform the array so that it contains only age like this
Collection {#231 ▼
#items: array:6 [▼
0 => 90
1 => 80
2 => 79
3 => 77
4 => 87
5 => 31
]
}
You can also use map method on collection
$collection = collect([0 => "1928-11-18",
1 => "1938-06-15",
2 => "1939-03-30",
3 => "1941-11-08",
4 => "1940-04-29",
5 => "1987-06-24"]);
$age = $collection->map(function ($item, $key) {
return Carbon::parse($item)->diff(Carbon::now())->format('%y');
});
return $age->all();
This will give you,
[
"89",
"79",
"79",
"76",
"78",
"30"
]
You can iterate trough a Collection with foreach() then convert to Age using Carbon ->age:
foreach ($collection as $key => $value) {
$collection[$key] = Carbon::parse($collection[$key])->age;
}
This will give you a Collection as what you do is editing the Collection, instead of creating a new array.
Use Carbon's ->age or diffInYears and collection transform (or map):
$dates->transform(function ($date) {
return \Carbon\Carbon::parse($date)->age;
});
Note: someone born on 1928-11-18 is 89 years old, not 90.

Creating Arrays of Parents and Children

I have an array, and I know the desired output, but I can't quite get my head around how to achieve it
The initial indexes are the "parent" id's, and the arrays contained within are the children, but some children are also parents
My array:
```
array:10 [▼
0 => array:2 [▼
0 => 1
1 => 5
]
1 => array:1 [▼
0 => 2
]
2 => array:2 [▼
0 => 3
1 => 4
]
5 => array:1 [▼
0 => 6
]
6 => array:2 [▼
0 => 7
1 => 8
]
9 => array:2 [▼
0 => 10
1 => 22
]
10 => array:1 [▼
0 => 11
]
11 => array:10 [▼
0 => 12
1 => 13
2 => 14
3 => 15
4 => 16
5 => 17
6 => 18
7 => 19
8 => 20
9 => 21
]
22 => array:1 [▼
0 => 23
]
23 => array:2 [▼
0 => 24
1 => 25
]
]
```
The Resulting array:
```
0
9
0,1
0,5
0,1,2
0,1,2,3
0,1,2,4
0,5,6
0,5,6,7
0,5,6,8
9,10
9,22
9,10,11
9,10,11,12
9,10,11,13
9,10,11,14
9,10,11,15
9,10,11,16
9,10,11,17
9,10,11,18
9,10,11,19
9,10,11,20
9,10,11,21
9,22,23
9,22,23,24
9,22,23,25
```
I'm thinking to use recursion, but I can't for the life of me figure out how to get the result
Edit: Further information:
If I take:
22 => array:1 [▼
0 => 23
]
23 => array:2 [▼
0 => 24
1 => 25
]
23 is the parent of 24 and 25, , 22 is the parent of 23, therefore 22 is the grandparent of 24 and 25:
9,22,23,24
9,22,23,25
That's how you get the list (9 is obviously the parent of 22, therefore the grandparent of 23 and great grandparent of 24 and 25)
This is a bit advanced stuff but you can use Iterators to achieve this.
First, we have to extend RecursiveIteratorIterator to serve our need:
class FlatRecursiveIteratorIterator extends RecursiveIteratorIterator
{
private $directory;
public function __construct(
Traversable $iterator,
$mode = RecursiveIteratorIterator::LEAVES_ONLY,
$flags = 0,
array $directory
) {
// Set array for children lookup.
$this->directory = $directory;
parent::__construct($iterator, $mode, $flags);
}
public function callHasChildren()
{
if ($this->getDepth() === 0 && is_array($this->current())) {
return true;
}
// See if children array availale in the top most array
// (lookup by id).
return !empty($this->directory[$this->current()]) &&
is_array($this->directory[$this->current()]);
}
public function callGetChildren()
{
return new RecursiveArrayIterator(
$this->getDepth() === 0
? $this->current()
: $this->directory[$this->current()]
);
}
}
Having this class we use it in combination with RecursiveArrayIterator and RecursiveCallbackFilterIterator to create needed $iterator:
$skip = [];
$iterator = new FlatRecursiveIteratorIterator(
// This filter helps up skip top level array elements
// if they have ancestors. Pay attencion that $skip passed by reference.
new RecursiveCallbackFilterIterator(
new RecursiveArrayIterator($array),
function ($current, $key, $_) use (&$skip) {
return !(is_array($current) && isset($skip[$key]));
}
),
RecursiveIteratorIterator::SELF_FIRST,
0,
$array
);
Then we can traverse this $iterator with a pretty simple non-nested loop:
$stack = [];
foreach ($iterator as $node) {
$depth = $iterator->getDepth();
if ($depth > 0) {
$skip[$node] = true;
}
// Use ternary operator as top most array has ids as keys,
// but inner arrays have ids as values.
$stack[$depth] = $depth === 0
? $iterator->key()
: $node;
// We have to slice out stack, as it may contain elements
// from previous iterations.
echo implode(',', array_slice($stack, 0, $depth + 1)), PHP_EOL;
}
Here is working demo.
This might look like a lot of code, but actually, it is not. And we need to take into account that most of the work is done by Standard PHP Library. We only have written the most case specific code.

Fill multidimensional array from other array

I have a multidimensional array $elements where I need to fill it with values from the array $ratings. The array $ratings is built so the first value will fit into the first slot in elements, the next in the second and so on.
$elements
4 => array:3 [▼
2 => 0
3 => 0
4 => 0
]
5 => array:3 [▼
2 => 0
3 => 0
4 => 0
]
7 => array:3 [▼
2 => 0
3 => 0
4 => 0
]
I now need to fill $elements with 9 specific values from
$ratings
array:9 [▼
0 => 3
1 => 2
2 => 1
3 => 3
4 => 3
5 => 2
6 => 3
7 => 2
8 => 1
9 => 3
]
If I manage to loop through $elements, inserting values from $ratings one by one, I will have solved my problem.
So $elements[4][2] should have the value of 3, $elements[4][3] should have value of 2 etc.
Also you can manipulate these by array_fill using loop.
Try this:
<?php
$elements = [
4=>[2=>0, 3=>0, 4=>0],
5=>[2=>0, 3=>0, 4=>0],
7=>[2=>0, 3=>0, 4=>0],
];
$ratings = [ 0 => 3, 1 => 2, 2 => 1, 3 => 3, 4 => 3, 5 => 2, 6 => 3, 7 => 2, 8 => 1, 9 => 3 ];
$ratingsIndex = 0;
foreach(array_keys($elements) as $ElementsIndex) {
foreach(array_keys($elements[$ElementsIndex]) as $ElementsSubIndex) {
$elements[$ElementsIndex][$ElementsSubIndex] = $ratings[$ratingsIndex++];
}
}
echo "<pre>";
print_r($elements);
echo "</pre>";
?>

Categories