Laravel get group of arrays - php

I have code below and result is like:
array:2 [▼
0 => array:1 [▼
"CPU" => "AMD a5"
]
1 => array:1 [▼
"CPU" => "AMD a9"
]
]
I want to group them in same array like
CPU => [
"0" => "AMD a5",
"1" => "AMD a9"
]
Code
$category = Category::where('slug', $slug)->where('status', '=', 'active')->first();
$products = $category->products;
foreach ($products as $product) {
foreach($product->attributes as $attribut){
$attributes[] = [$attribut->group->title => $attribut->title];
}
}
What should I change?

If you want a bit of a more scalable solution where you might have multiple attribute types, the following may help:
$attributes = [
[
'CPU' => 'AMD A5'
],
[
'CPU' => 'AMD A9'
],
[
'GFX' => 'AMD'
]
];
return collect($attributes)
->groupBy(function($item) {
return key($item);
})
->map(function($item) {
return array_flatten($item);
})
->toArray();
The output of the above will be:
array:2 [▼
"CPU" => array:2 [▼
0 => "AMD A5"
1 => "AMD A9"
]
"GFX" => array:1 [▼
0 => "AMD"
]
]
Here's an example you can play with.

if your $attribut->group->title is "CPU" and your $attribut->title is "AMD a5"
then you can use this
foreach ($products as $product) {
foreach($product->attributes as $attribut){
$attributes[$attribut->group->title][] = $attribut->title;
}
}
from #mozammil answer if you still wanna do using foreach
$attributes =
[
[
'CPU' => 'AMD A5'
],
[
'CPU' => 'AMD A9'
],
[
'GFX' => 'AMD'
]
];
$data = [];
foreach ($attributes as $titles){
foreach ($titles as $title=> $row){
$data[$title][] = $row;
}
}

You can try collection methods here is an example.
$keyed = $collection->map(function ($item,$key) {
return [[$key] => $item['CPU']];
});
For more information
https://laravel.com/docs/5.7/collections#method-map

Use array_column to get one column of an array.
$category = Category::where('slug', $slug)->where('status', '=', 'active')->first();
$products = $category->products;
$cpu = array_column($products, "CPU");
This will return an array as your expected result.
https://3v4l.org/UYPhK
This requires PHP 7 since in older versions array_column can't handle objects

Related

Transform data with Laravel helpers

I have an array with the following format:
[
{
"stat_month": "01-2019",
"in_sum": 45443,
"out_sum": 42838,
"balance": 2605
},
{...}
]
But I want to transform this array, hopefully in one operation, to this:
[
"labels" => ["01-2019", "02-2019", "03-2019"],
"in_sum" => [45443, 60947, 56734],
"out_sum" => [42838, 42151, 75486],
"balance" => [2605, 18796, -18752]
]
Any ideas how to solve this in one operation with collection helper functions?
Look at mapToGroups in Laravel Collections:
https://laravel.com/docs/5.8/collections#method-maptogroups
or this solution:
$obj1 = new \stdClass;
$obj1->stat_month = "01-2019";
$obj1->in_sum = 45443;
$obj1->out_sum = 42838;
$obj1->balance = 2605;
$obj2 = new \stdClass;
$obj2->stat_month = "02-2019";
$obj2->in_sum = 55443;
$obj2->out_sum = 52838;
$obj2->balance = 3605;
$collection = collect([
$obj1,$obj2
]);
$aResult = [
'labels' => [],
'in_sum' => [],
'out_sum' => [],
'balance' => []
];
$collection->each(function ($item, $key) use (&$aResult) {
$aResult['labels'][] = $item->stat_month;
$aResult['in_sum'][] = $item->in_sum;
$aResult['out_sum'][] = $item->out_sum;
$aResult['balance'][] = $item->balance;
});
Result:
array:4 [▼
"labels" => array:2 [▼
0 => "01-2019"
1 => "02-2019"
]
"in_sum" => array:2 [▼
0 => 45443
1 => 55443
]
"out_sum" => array:2 [▼
0 => 42838
1 => 52838
]
"balance" => array:2 [▼
0 => 2605
1 => 3605
]
]
You can do this is php like this:
<?php
$array = '[
{
"stat_month": "01-2019",
"in_sum": 45443,
"out_sum": 42838,
"balance": 2605
},
{
"stat_month": "01-2019",
"in_sum": 45443,
"out_sum": 42838,
"balance": 2605
},
{
"stat_month": "01-2019",
"in_sum": 45443,
"out_sum": 42838,
"balance": 2605
}
]';
$array = json_decode($array, true);
$arrayResult = [
'stat_month' => array_column($array, 'stat_month'),
'in_sum' => array_column($array, 'in_sum'),
'out_sum' => array_column($array, 'out_sum'),
'balance' => array_column($array, 'balance')
];
echo "<pre>";
print_r($arrayResult);
?>
You can use simplified array_map() and receive desired output:
$i = 0;
$tmp = ['labels'=>[],'in_sum'=>[],'out_sum'=>[],'balance'=>[]];
array_map(function($obj) use (&$tmp, $i){
foreach($tmp as &$val){
$val[] = array_values(((array)$obj))[$i];
$i++;
}
},$ar);
Demo
Or just simple foreach loop:
$tmp = ['labels'=>[],'in_sum'=>[],'out_sum'=>[],'balance'=>[]];
foreach($ar as $obj) {
$i = 0;
foreach($tmp as &$val){
$val[] = array_values(((array)$obj))[$i];
$i++;
}
}
Demo2
You can replace and re-write this code with Laravel map() function. Try to use dynamic loops instead of predefined object's properties.

How map array of array to single assoc array?

I have:
array:2 [
0 => array:1 [
"FNAME" => "nullable|string"
]
1 => array:1 [
"LNAME" => "nullable|string"
]
]
And I try to get:
array:1 [
"key" => "value"
]
I try map it, but has problem
<?php
$array = [
[
"FNAME" => "nullable|string",
],
[
"LNAME" => "nullable|string",
]
];
$newArray = [];
foreach ($array as $item) {
foreach ($item as $key => $value) {
$newArray[$key] = $value;
}
}
print_r($newArray);
Will output:
Array
(
[FNAME] => nullable|string
[LNAME] => nullable|string
)
Two simple ways:
print_r(array_merge(...$arr));
// if `...` is not available (php < 5.6), then:
print_r(call_user_func_array('array_merge', $arr))

How to navigate back up in a multidimensional array after finding the appropriate child?

I have an array that looks like this:
RecursiveArrayIterator {#605 ▼
+"xs:schema": array:2 [▼
"value" => array:1 [▼
"xs:element" => array:2 [▼
"value" => array:1 [▼
"xs:complexType" => array:2 [▼
"value" => array:2 [▼
"xs:sequence" => array:2 [▼
"value" => array:1 [▼
"xs:element" => array:3 [▼
0 => array:2 [▼
"value" => array:1 [▼
"xs:simpleType" => array:2 [▼
"value" => array:1 [▼
"xs:restriction" => array:2 [▼
"value" => array:1 [▼
"xs:maxLength" => array:1 [▼
"attributes" => array:1 [▼
"value" => "40"
]
]
]
"attributes" => array:1 [▶]
]
]
"attributes" => []
]
]
"attributes" => array:1 [▼
"name" => "title"
]
]
1 => array:2 [▶]
2 => array:2 [▶]
]
]
"attributes" => []
]
"xs:attribute" => array:2 [▶]
]
"attributes" => []
]
]
"attributes" => array:1 [▼
"name" => "book"
]
]
]
"attributes" => []
]
}
I need to access the xs:maxLength attribute, so in order to that that, I'm using the following method:
private function findRestrictions(array $haystack, $needle)
{
$iterator = new \RecursiveArrayIterator($haystack);
$recursive = new \RecursiveIteratorIterator(
$iterator,
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($recursive as $key => $value)
{
if ($key === $needle)
{
return (int)$value['attributes']['value'];
}
}
}
$maxLength = findRestrictions($array, 'xs:maxLength');
So that gives me back 40, just like expected. Anyway, my issue is that I need to know to which element this limit belongs to, which is mentioned in xs:element[0]['attributes']['name'] and I'm uncertain on how to reach there to grab the information I need, based on the match for xs:maxLength.
Well I have programmed a pretty good solution I think, this time it is tested.
My sample array:
$array = [
"we" =>
["are" => [
"lorem" => [
"gone" => "away",
"my" => "friend"
],
"never" => "abcd",
"any" => [
"btc" => "abc",
"help" => [
"mqf" => "bmx"
]
]
]
],
"fancy" => [
"lorem" => [
"gone" => "away",
"my" => "friend"
],
"never" => "abcd",
"any" => [
"btc" => "abc",
"help" => [
"mqf" => "bmx",
"abc" => 13
]
]
],
"beer" => "bar",
"helpful" => [
"meta" => "column",
"gamma" => [
"lorem" => [
"gone" => "mad",
"my" => "drink"
],
"never" => "abcd",
"any" => [
"btc" => "abc",
"help" => [
"mqf" => "bmx",
"abc" => "alot"
]
]
]
],
"elements" => [
0 => 88,
1 => 99
]
];
My solution:
function array_find_value_return_parent($array,$needle,$parentkey) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $key => $value) {
if($value === $needle) {
for ($i = $iterator->getDepth() - 1; $i >= 0; $i--) {
if($iterator->getSubIterator($i)->key() === $parentkey) {
return $iterator->getSubIterator($i)->current();
}
}
}
}
}
function array_find_key_return_value($array,$findkey) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $key => $value) {
if($findkey === $key) {
return $iterator->current();
}
}
}
My test:
$findvalue = "alot";
$findparentkey = "gamma";
$findreturnkey = "gone";
echo array_find_key_return_value(array_find_value_return_parent($array,$findvalue,$findparentkey),$findreturnkey);
Output: mad
For your case it means that you might do the following:
$findvalue = "40";
$findparentkey = "xs:element";
$findreturnkey = "name";
echo array_find_key_return_value(array_find_value_return_parent($array,$findvalue,$findparentkey),$findreturnkey);
Expected output: title
Right?
I do not know your original data structure, so I just converted your data to a PHP array. You can use $aks = new ArrayKeySearcher($data, 'xs:maxLength'); to find the key you want. And you can make the search more complex to satisfy your requirement.
However, if you are using something like XML, it is highly recommended to use XML-based solutions, like XPath query (eg: http://php.net/manual/en/domxpath.query.php, http://php.net/manual/en/simplexmlelement.xpath.php). These methods are easier to use, faster and more accurate.
<?php
$data = [
"xs:schema"=> [
"value" => [
"xs:element" => [
"value" => [
"xs:complexType" => [
"value" => [
"xs:sequence" => [
"value" => [
"xs:element" => [
0 => [
"value" => [
"xs:simpleType" => [
"value" => [
"xs:restriction" => [
"value" => [
"xs:maxLength" => [
"attributes" => [
"value" => "40"
]
]
],
"attributes" => []
]
],
"attributes" => []
]
],
"attributes" => [
"name" => "title"
]
],
1 => [],
2 => [],
]
],
"attributes" => []
],
"xs:attribute" => []
],
"attributes" => []
]
],
"attributes" => [
"name" => "book"
]
]
],
"attributes" => []
]
];
class ArrayKeySearcher
{
public $data;
public $path;
public $value;
public function __construct($data, $key)
{
$this->data = $data;
$this->findKeyPath($data, $key);
}
private function findKeyPath($data, $key)
{
foreach ($data as $k => $v) {
$this->path[] = $k;
if ($key === $k) {
$this->value = $v;
return;
}
$this->findKeyPath($v, $key);
if (!is_null($this->value))
return;
array_pop($this->path);
}
}
public function arrayReverseSearch($a, $k, $pos = null)
{
$count = count($a);
$i = ($pos === null) ? ($count - 1) : $pos;
for(; $i >= 0; $i--) {
if($a[$i] === $k)
return $i;
}
return $i;
}
public function getValueByPath($path)
{
$v = $this->data;
foreach($path as $k) {
if(isset($v[$k]))
$v = $v[$k];
}
return $v;
}
}
$aks = new ArrayKeySearcher($data, 'xs:maxLength');
echo 'path: ' . json_encode($aks->path) . PHP_EOL;
echo 'value: ' . json_encode($aks->value) . PHP_EOL;
$p = $aks->path;
$pos = $aks->arrayReverseSearch($p, 'xs:simpleType');
$pos = $aks->arrayReverseSearch($p, 'value', $pos);
$p = array_slice($p, 0, $pos);
$parent = $aks->getValueByPath($p);
echo 'parent path: ' . json_encode($p) . PHP_EOL;
echo 'parent attributes: ' . json_encode($parent['attributes']) . PHP_EOL;
Output:
path: ["xs:schema","value","xs:element","value","xs:complexType","value","xs:sequence","value","xs:element",0,"value","xs:simpleType","value","xs:restriction","value","xs:maxLength"]
value: {"attributes":{"value":"40"}}
parent path: ["xs:schema","value","xs:element","value","xs:complexType","value","xs:sequence","value","xs:element",0]
parent attributes: {"name":"title"}

How to parse a laravel collection

I have a laravel collection on output, I want to parse it ->toArray()
Collection {#335
#items: array:2 [
"0f39b1e0-a507-11e7-9d6e-33e84951047e" => array:2 [
"total_amount" => 25000
"debt_type" => array:2 [
0 => "car_loan"
1 => "car_loan"
]
]
"0f218520-a507-11e7-b0ba-8554a4ad039b" => array:2 [
"total_amount" => 15000
"debt_type" => array:1 [
0 => "house_loan"
]
]
]
}
is there any way to parse it so I get the following output:
array:1[
0=>[
'debt_id'=>'0f39b1e0-a507-11e7-9d6e-33e84951047e',
'debt_type'=>'car_loan',
'total_amount'=>25000
],
1=>[
'debt_id'=>'0f218520-a507-11e7-b0ba-8554a4ad039b',
'debt_type'=>'house_loan',
'total_amount'=>15000
]
]
what I have tried it works but not sure if its a good way to go around it:
$appDebts = $appDebts->groupBy('debt_type_id')->map(function ($item) {
return [
'total_amount' => $item->sum('amount'),
'debt_type' => $item->map(function ($item) {
return $item->debt_type->slug;
})->toArray(),
];
})->toArray();
if you dd $appDebts you get the collection that I have added on top of the post
$carLoan = [];
$studentLoan = [];
$houseLoan = [];
$cardLoan = [];
foreach ($appDebts as $debt) {
if ($debt['debt_type'][0] === 'car_loan') {
$carLoan['TotalAmount'] = $debt['total_amount'];
$carLoan['LoanType'] = $debt['debt_type'][0];
}
if ($debt['debt_type'][0] === 'house_loan') {
$houseLoan['TotalAmount'] = $debt['total_amount'];
$houseLoan['LoanType'] = $debt['debt_type'][0];
}
if ($debt['debt_type'][0] === 'student_loan') {
$studentLoan['TotalAmount'] = $debt['total_amount'];
$studentLoan['LoanType'] = $debt['debt_type'][0];
}
if ($debt['debt_type'][0] === 'credit_card_loan') {
$cardLoan['TotalAmount'] = $debt['total_amount'];
$cardLoan['LoanType'] = $debt['debt_type'][0];
}
}
Based on the array you shared:
$parsed = $collection->map(function ($item, $id) {
return [
'debt_id' => $id,
'debt_type' => collect($item['debt_type'])->first(),
'total_amount' => $item['total_amount']
];
})->values()->toArray();
With values you remove the key => value, you get the array without keys
Try with this mapping after the first one that you did :
$appDebts = $appDebts->groupBy('debt_type_id')->map(function ($item) {
return [
'total_amount' => $item->sum('amount'),
'debt_type' => $item->map(function ($item) {
return $item->debt_type->slug;
})->toArray(),
];
}); // <-- remove ->toArray() from here
$appDebts = $appDebts->map(function ($item, $key) {
return [
'debt_type_id' => $key
'debt_type' => $item["debt_type"][0], // assuming you want the first type !!
'total_amount' => $item["total_amount"],
];
})->toArray();
PS : This convert the given collection to tha wanted array for more performance tweaking consider editing the SQL query or the logic of getting appDebts
The only thing I can add to #Llopele's answer is to use keyBy() for easier data access:
$parsed = $collection->map(function ($item, $id) {
return [
'debt_id' => $id,
'debt_type' => collect($item['debt_type'])->first(),
'total_amount' => $item['total_amount']
];
})->values()->keyBy('debt_type')->toArray();
So now you can access data like this Arr::get($parsed, 'house_loan');

Laravel save multiple records

I've got an array within arrays and would like to add something to it.
$options = $request->options;
foreach ($options as $option) {
$option['poll_id'] = $this->id;
}
dd($options);
But for some reason it does not add to the array.
So I receive this:
array:1 [
0 => array:1 [
"name" => "testtest"
]
]
But I would expect this:
array:1 [
0 => array:1 [
"name" => "testtest",
"poll_id" => 1
]
]
You're not changing $options so foreach is destroying $option with each iteration. Try something like this instead:
$options = [];
foreach ($request->options as $key => $value) {
$options[$key]['poll_id'] = $this->id;
}
You should do it using the $key attribute on arrays
// Suppose your $request->options is like:
$options = [
0 => [
"name" => "testtest"
]
];
foreach ($options as $key => $option) {
$options[$key]['poll_id'] = 3; // Changing variable - $options here.
}
and it should work!
// $options would be like:
array:1 [▼
0 => array:2 [▼
"name" => "testtest"
"poll_id" => 3
]
]

Categories