php multidimensional array get values - php

This is my array in php $hotels
Array
(
[0] => Array
(
[hotel_name] => Name
[info] => info
[rooms] => Array
(
[0] => Array
(
[room_name] => name
[beds] => 2
[boards] => Array
(
[board_id] => 1
[price] =>200.00
)
)
)
)
)
How can I get board_id and price I have tried few foreach loops but can't get the result
foreach($hotels as $row)
{
foreach($row as $k)
{
foreach($k as $l)
{
echo $l['board_id'];
echo $l['price'];
}
}
}
This code didn't work.

This is the way to iterate on this array:
foreach($hotels as $row) {
foreach($row['rooms'] as $k) {
echo $k['boards']['board_id'];
echo $k['boards']['price'];
}
}
You want to iterate on the hotels and the rooms (the ones with numeric indexes), because those seem to be the "collections" in this case. The other arrays only hold and group properties.

For people who searched for php multidimensional array get values and actually want to solve problem comes from getting one column value from a 2 dimensinal array (like me!), here's a much elegant way than using foreach, which is array_column
For example, if I only want to get hotel_name from the below array, and form to another array:
$hotels = [
[
'hotel_name' => 'Hotel A',
'info' => 'Hotel A Info',
],
[
'hotel_name' => 'Hotel B',
'info' => 'Hotel B Info',
]
];
I can do this using array_column:
$hotel_name = array_column($hotels, 'hotel_name');
print_r($hotel_name); // Which will give me ['Hotel A', 'Hotel B']
For the actual answer for this question, it can also be beautified by array_column and call_user_func_array('array_merge', $twoDimensionalArray);
Let's make the data in PHP:
$hotels = [
[
'hotel_name' => 'Hotel A',
'info' => 'Hotel A Info',
'rooms' => [
[
'room_name' => 'Luxury Room',
'bed' => 2,
'boards' => [
'board_id' => 1,
'price' => 200
]
],
[
'room_name' => 'Non Luxy Room',
'bed' => 4,
'boards' => [
'board_id' => 2,
'price' => 150
]
],
]
],
[
'hotel_name' => 'Hotel B',
'info' => 'Hotel B Info',
'rooms' => [
[
'room_name' => 'Luxury Room',
'bed' => 2,
'boards' => [
'board_id' => 3,
'price' => 900
]
],
[
'room_name' => 'Non Luxy Room',
'bed' => 4,
'boards' => [
'board_id' => 4,
'price' => 300
]
],
]
]
];
And here's the calculation:
$rooms = array_column($hotels, 'rooms');
$rooms = call_user_func_array('array_merge', $rooms);
$boards = array_column($rooms, 'boards');
foreach($boards as $board){
$board_id = $board['board_id'];
$price = $board['price'];
echo "Board ID is: ".$board_id." and price is: ".$price . "<br/>";
}
Which will give you the following result:
Board ID is: 1 and price is: 200
Board ID is: 2 and price is: 150
Board ID is: 3 and price is: 900
Board ID is: 4 and price is: 300

Related

Create new column in 2d array that represents the count of related values in another 2d array

I have an array that contains data of incoming mail, looks something like this:
$incomingMail = [
0 => [
'title' => 'Title 1',
'areaCode' => 101
],
1 => [
'title' => 'Title 2',
'areaCode' => 101
],
2 => [
'title' => 'Title 3',
'areaCode' => 102
]
];
And another array containing area name and code, the array looks like this:
$areaArr = [
0 => [
'name' => 'Area 1',
'code' => 101
],
1 => [
'name' => 'Area 2',
'code' => 102
],
2 => [
'name' => 'Area 3',
'code' => 103
],
3 => [
'name' => 'Area 4',
'code' => 104
]
];
I want to create an array that contains the count of incomingMail array based on areaArr's code, it will kind of looks like this:
$areaWithMailCount = [
0 => [
'areaName' => 'Area 1',
'areaCode' => 101,
'mailCount' => 2
],
1 => [
'areaName' => 'Area 2',
'areaCode' => 102,
'mailCount' => 1
],
2 => [
'areaName' => 'Area 3',
'areaCode' => 103,
'mailCount' => 0
],
3 => [
'areaName' => 'Area 4',
'areaCode' => 104,
'mailCount' => 0
]
];
I have tried to loop those arrays and add the condition based on area code but the result isn't quite what I wanted, the code looks something like this:
$areaWithMailCount = [];
foreach($areaArr as $area) {
foreach($incomingMail as $mail) {
if($mail['areaCode'] == $area['code']) {
$areaWithMailCount[] = [
'areaName' => $area['name'],
'areaCode' => $area['code'],
'mailCount' => count($mail)
];
}
}
}
The result from above code is like this:
[
0 => [
"areaName" => "Area 1"
"areaCode" => 101
"mailCount" => 2
],
1 => [
"areaName" => "Area 1"
"areaCode" => 101
"mailCount" => 2
],
2 => [
"areaName" => "Area 2"
"areaCode" => 102
"mailCount" => 2
]
];
Any ideas how to achieve that?
I know this has been answered but just to add a bit of a cleaner way.
This is essentially using the same process as https://stackoverflow.com/a/75443993/1791606 but is too big for a comment so I've stuck it in an answer.
$mailCounts = array_count_values(array_column($incomingMail, 'areaCode'));
$areaWithMailCount = array_map(fn(array $area) => [
'areaName' => $area['name'],
'areaCode' => $area['code'],
'mailCount' => $mailCounts[$area['code']] ?? 0,
], $areaArr);
It is inefficient to use nested loops -- this will make your server do loads of unnecessary cycles while searching for qualifying values.
Instead, loop the $incomingMail array only once and populate a lookup array which the $areaArr array can reference as it is iterated. If the code in the $areaArr does not related to any keys in the lookup array, then default the value to 0.
Code: (Demo)
$lookup = [];
foreach ($incomingMail as $row) {
$lookup[$row['areaCode']] = ($lookup[$row['areaCode']] ?? 0) + 1;
}
$result = [];
foreach ($areaArr as $row) {
$result[] = [
'areaName' => $row['name'],
'areaCode' => $row['code'],
'mailCount' => $lookup[$row['code']] ?? 0
];
}
var_export($result);
The functional-style equivalent of the above 2-loop snippet looks like this: Demo
$lookup = array_reduce(
$incomingMail,
function($result, $row) {
$result[$row['areaCode']] = ($result[$row['areaCode']] ?? 0) + 1;
return $result;
}
);
var_export(
array_map(
fn($row) =>
[
'areaName' => $row['name'],
'areaCode' => $row['code'],
'mailCount' => $lookup[$row['code']] ?? 0
],
$areaArr
)
);
Or even: Demo
var_export(
array_map(
function($row) use ($incomingMail) {
static $lookup;
$lookup ??= array_reduce(
$incomingMail,
function($result, $row) {
$result[$row['areaCode']] = ($result[$row['areaCode']] ?? 0) + 1;
return $result;
}
);
return [
'areaName' => $row['name'],
'areaCode' => $row['code'],
'mailCount' => $lookup[$row['code']] ?? 0
];
},
$areaArr
)
);

Counting expenses for categories stored in tree like array

I have an app where users can store their expenses and assign it to categories. Categories have tree structure, without depth limit and users can assign expense to any category (children, parent or middle). What I want to do is to count sum of expenses for all categories in tree structure. Lets simplify things and assume I have array with categories:
$categories = [
0 => [
'id' => 1,
'name' => 'Car',
'expenses_sum' => 0,
'subcategories' => [
0 => [
'id' => 2,
'name' => 'Gas',
'expenses_sum' => 0,
'subcategories' => [
0 => [
'id' => 3,
'name' => 'Orlen',
'expenses_sum' => 300,
'subcategories' => [
0 => [
'id' => 4,
'name' => 'Orlen A',
'expenses_sum' => 100,
'subcategories' => []
],
1 => [
'id' => 5,
'name' => 'Orlen B',
'expenses_sum' => 100,
'subcategories' => []
],
],
],
],
],
],
],
];
What I want do do is to go to the deepest level ("Orlen A" and "Orlen B"), sum expenses from that level, add it to the parent level ("Orlen") and store as the new "expenses_sum", so it should be: 100 + 100 + 300 = 500 and repeat it to the top level.
My code to do that looks like this:
function sumCategory(&$category) {
if (isset($category['subcategories'])) {
foreach ($category['subcategories'] as $subCategory) {
$subCategory['expenses_sum'] = sumCategory($subCategory);
$category['expenses_sum'] += $subCategory['expenses_sum'];
}
}
return $category['expenses_sum'];
}
foreach ($categories as $index => $category) {
$categories[$index]['expenses_sum'] = sumCategory($category);
}
echo '<pre>';
print_r($categories);
echo '</pre>';
Code counts properly sum at all levels, but the problem is that finally I got $categories array with modfied only "expenses_sum" at the top level and categories in the middle stays not modified. I tried to pass $categories to function by refference, but it doesnt help. Any tips what should I correct?
I have simplified problem, so you can easily grab all above code to test things on your own.
To clarify things: at the end I expect my array will be as follow:
$categories = [
0 => [
'id' => 1,
'name' => 'Car',
'expenses_sum' => 500, //500 + 0
'subcategories' => [
0 => [
'id' => 2,
'name' => 'Gas',
'expenses_sum' => 500, //500 + 0
'subcategories' => [
0 => [
'id' => 3,
'name' => 'Orlen',
'expenses_sum' => 500, //100 + 100 + 300
'subcategories' => [
0 => [
'id' => 4,
'name' => 'Orlen A',
'expenses_sum' => 100,
'subcategories' => []
],
1 => [
'id' => 5,
'name' => 'Orlen B',
'expenses_sum' => 100,
'subcategories' => []
],
],
],
],
],
],
],
];
$categories = [
0 => [
'id' => 1,
'name' => 'Car',
'expenses_sum' => 0,
'subcategories' => [
0 => [
'id' => 2,
'name' => 'Gas',
'expenses_sum' => 0,
'subcategories' => [
0 => [
'id' => 3,
'name' => 'Orlen',
'expenses_sum' => 300,
'subcategories' => [
0 => [
'id' => 4,
'name' => 'Orlen A',
'expenses_sum' => 100,
'subcategories' => []
],
1 => [
'id' => 5,
'name' => 'Orlen B',
'expenses_sum' => 100,
'subcategories' => []
],
],
],
],
],
],
],
];
function expensesSum($category) {
$sum = $category['expenses_sum'] ;
foreach($category['subcategories'] as $subCategory) {
$sum += expensesSum($subCategory);
}
return $sum;
}
function calculateExpenses(&$categories) {
foreach($categories as $index => $category) {
// do subs first
calculateExpenses($categories[$index]['subcategories']);
// then parent
$categories[$index]['expenses_sum'] = expensesSum($category);
}
return $categories;
}
print_r(calculateExpenses($categories));
Output:
Array
(
[0] => Array
(
[id] => 1
[name] => Car
[expenses_sum] => 500
[subcategories] => Array
(
[0] => Array
(
[id] => 2
[name] => Gas
[expenses_sum] => 500
[subcategories] => Array
(
[0] => Array
(
[id] => 3
[name] => Orlen
[expenses_sum] => 500
[subcategories] => Array
(
[0] => Array
(
[id] => 4
[name] => Orlen A
[expenses_sum] => 100
[subcategories] => Array
(
)
)
[1] => Array
(
[id] => 5
[name] => Orlen B
[expenses_sum] => 100
[subcategories] => Array
(
)
)
)
)
)
)
)
)
)
function calculateExpenses($categories) {
$expenses = 0;
foreach($categories as $category) {
$expenses += $category['expenses_sum'];
$expenses += calculateExpenses($category['subcategories']);
}
return $expenses;
}
echo calculateExpenses($categories);

PHP create array based on date

I'm trying to create an dynamic array sorted on date. Let me try to explain. I've the follow user array
$users = [
0 => [
'user_id' => 1,
'user_date' => '2017-04-26',
'user_name' => 'test',
],
1 => [
'user_id' => 2,
'user_date' => '2017-04-26',
'user_name' => 'test 2',
],
2 => [
'user_id' => 3,
'user_date' => '2017-04-28',
'user_name' => 'test 3',
]
];
While looping throug this array a want to group the users that has the same date. An example how the output should look like
Array
(
[0] => Array
(
[DATE] => 2017-04-26
[USERS] => Array
(
[0] => Array
(
[user_id] => 1
[user_title] => test
)
[1] => Array
(
[user_id] => 2
[user_title] => test 2
)
)
)
[1] => Array
(
[DATE] => 2017-04-28
[USERS] => Array
(
[0] => Array
(
[user_id] => 4
[user_title] => test 4
)
)
)
)
I have tried to do some things in a foreach loop but could not make this get to work.
$result = array();
$i = 0;
// Start loop
foreach ($users as $user) {
// CHECK IF DATE ALREADY EXISTS
if(isset($result[$i]['DATE']) && $result[$i]['DATE'] == $user['user_date']){
$i++;
}
// FILL THE ARRAY
$result[$i] = [
'DATE' => $user['user_date'],
'USERS' => [
'user_id' => $user['user_id'],
'user_title' => $user['user_name'],
]
];
}
I've changed it a little bit to this:
foreach ($users as $user => $properties) {
foreach ($properties as $property => $value) {
if($property == 'user_date'){
if(empty($result[$value])){
$result[$i] = [];
}
$result[$i][] = [
'user_id' => $properties['user_id'],
'user_name' => $properties['user_name'],
];
$i++;
}
}
}
But how could i change the start keys (dates) to numbers equal to 0, 1 etc.
$users = [
0 => [
'user_id' => 1,
'user_date' => '2017-04-26',
'user_name' => 'test',
],
1 => [
'user_id' => 2,
'user_date' => '2017-04-26',
'user_name' => 'test 2',
],
2 => [
'user_id' => 3,
'user_date' => '2017-04-28',
'user_name' => 'test 3',
]
];
$sorted = [];
foreach ($users as $user => $properties) {
foreach ($properties as $property => $value) {
if ($property =='user_date') {
if (empty($sorted[$value])) {
$sorted[$value] = [];
}
$sorted[$value][] = $users[$user];
}
}
}
var_dump($sorted);
Do a nested loop through your arrays and then check for the unique value you're looking for (in this case the user_date) and add that as a key in your sorted array. If the key exists add a new item (user) to that key, otherwise make the new key first. This way you have an array of dates each containing an array of users with that date.
If you want the exact output you showed (honestly, I like Ryan's answer better):
$result = array();
$i = 0;
// Start loop
foreach ($users as $user) {
// CHECK IF DATE ALREADY EXISTS AND IS NOT IN THE SAME GROUP
if (isset($result[$i]['DATE']) && $result[$i]['DATE'] != $user['user_date']){
$i++;
}
// STARTING A NEW GROUP
if(!isset($result[$i])) {
$result[$i] = array(
'DATE' => $user['user_date'],
'USERS' => array()
);
}
// FILL THE ARRAY (note the ending [] to add a new entry in this group's USERS array)
$result[$i]['USERS'][] = array(
'user_id' => $user['user_id'],
'user_title' => $user['user_name'],
);
}
There are few ways to tackle your question. I always prefer to use PHP built-in function, as there are a lot of them. This answer uses a PHP builtin function usort to sort your array in place. It takes two arguments, you array and a comparator function. usort will parse two array object to comparator function. If you dont know about compactor functions, a Comparator compare these two objects and return a integer 1, 0, or -1 which tells if first object is greater, equal or less than second object, respectively. So pass in a comparator function to that takes care of the comparation of dates.
$users = [
0 => [
'user_id' => 1,
'user_date' => '2017-04-25',
'user_name' => 'test',
],
1 => [
'user_id' => 2,
'user_date' => '2017-04-26',
'user_name' => 'test 2',
],
2 => [
'user_id' => 3,
'user_date' => '2017-04-28',
'user_name' => 'test 3',
],
3 => [
'user_id' => 4,
'user_date' => '2017-04-28',
'user_name' => 'test 4',
],
4 => [
'user_id' => 5,
'user_date' => '2017-04-26',
'user_name' => 'test 5',
],
];
usort($users, function($user1, $user2){
// This function sort users by ascending order of date. Compares date. if user 1 has later date than user 2, place him on the bottom of the array
return strtotime($user1['user_date']) > strtotime($user2['user_date']);
});
var_dump($users);

Finding the lowest value for specific key in a deeply nested array

Given an array with the following structure:
$orders = [
0 => [
'volume' => 1926,
'lowPrice' => 1000.00,
'date' => 2016-12-29 00:45:23
],
1 => [
'volume' => 145,
'lowPrice' => 1009.99,
'date' => 2016-12-31 14:23:51
],
2 => [
'volume' => 19,
'lowPrice' => 985.99,
'date' => 2016-12-21 09:37:13
],
3 => [
'volume' => 2984,
'lowPrice' => 749.99,
'date' => 2017-01-01 19:37:22
],
// ...
]
I'm looking for the most performant way to find the lowest value for lowPrice. The size of $orders will more than likely be larger than 500 items. For the given array, the result would be as follows:
$lowestPrice = $this->findLowestPrice($orders);
echo $lowestPrice; // 749.99
Extract an array of lowPrice and find the min():
echo min(array_column($orders, 'lowPrice'));
Since those appear to be floats, you'll probably want number_format() or money_format().
If you're only interested in the value of the lowest price (not the index in the array or something) you can use array_reduce:
$orders = [
0 => [
'volume' => 1926,
'lowPrice' => 1000.00,
'date' => "2016-12-29 00:45:23"
],
1 => [
'volume' => 145,
'lowPrice' => 1009.99,
'date' => "2016-12-31 14:23:51"
],
2 => [
'volume' => 19,
'lowPrice' => 985.99,
'date' => "2016-12-21 09:37:13"
],
3 => [
'volume' => 2984,
'lowPrice' => 749.99,
'date' => "2017-01-01 19:37:22"
],
];
$lowestPrice = array_reduce(
$orders,
function($c, $v)
{
if ($c === null)
return $v["lowPrice"];
return $v["lowPrice"] < $c ? $v["lowPrice"] : $c;
}
);

PHP Merge by values in same array

So I have this array in PHP.
$arr = [
[ 'sections' => [1], 'id' => 1 ],
[ 'sections' => [2], 'id' => 1 ],
[ 'sections' => [3], 'id' => NULL ],
[ 'sections' => [4], 'id' => 4 ],
[ 'sections' => [5], 'id' => 4 ],
[ 'sections' => [6], 'id' => 4 ]
];
I want to merge on 'id' and get something like
$arr = [
[ 'sections' => [1, 2], 'id' => 1 ],
[ 'sections' => [3], 'id' => NULL ],
[ 'sections' => [4, 5, 6], 'id' => 4 ]
];
Just struggling to get my head around this one. Any Ideas
I've created this quick function that might work for you
<?php
// Your array
$arr = array(
array( 'elem1' => 1, 'elem2' => 1 ),
array( 'elem1' => 2, 'elem2' => 1 ),
array( 'elem1' => 3, 'elem2' => NULL ),
array( 'elem1' => 4, 'elem2' => 4 ),
array( 'elem1' => 5, 'elem2' => 4 ),
array( 'elem1' => 6, 'elem2' => 4 )
);
print_r($arr);
function mergeBy($arr, $elem2 = 'elem2') {
$result = array();
foreach ($arr as $item) {
if (empty($result[$item[$elem2]])) {
// for new items (elem2), just add it in with index of elem2's value to start
$result[$item[$elem2]] = $item;
} else {
// for non-new items (elem2) merge any other values (elem1)
foreach ($item as $key => $val) {
if ($key != $elem2) {
// cast elem1's as arrays, just incase you were lazy like me in the declaration of the array
$result[$item[$elem2]][$key] = $result[$item[$elem2]][$key] = array_merge((array)$result[$item[$elem2]][$key],(array)$val);
}
}
}
}
// strip out the keys so that you dont have the elem2's values all over the place
return array_values($result);
}
print_r(mergeBy($arr));
?>
Hopefully it'll work for more than 2 elements, and you can choose what to sort on also....

Categories