Loop multidimensional line by line - php

I'm trying to loop through this array to output side by side for every iteration.
$cars = [
'BMW' => [
'Year' => '2020',
'Body' => 'Sedan',
'Mileage' => '100'
],
'Ford' => [
'Year' => '2019',
'Body' => 'SUV',
'Mileage' => '500'
]
];
$count = count(reset($cars));
for($i=0; $i<$count; $i++) {
foreach($cars as $key => $value) {
echo $cars[$key][$i]. '<br>';
}
}
My expected result for every iteration.
2020,2019
Sedan,SUV
100,500
I have searched through some links, but none is specific to a multidimensional array.
Two arrays in foreach loop
How can I loop through two arrays at once?

The other question you point to is about having separate arrays of data, which you could sort of emulate by splitting the main array into parts.
This solution assumes that all of the keys for the values you want are in the first item (as in your example, they all have the 3 values) and loops over the first array element and uses array_column() to extract all of the values from all of the main array elements for that key. Then implode() these values to put them as a CSV list...
foreach ( $cars[array_keys($cars)[0]] as $key => $value) {
echo implode(",", array_column($cars, $key)).PHP_EOL;
}
which with your test data gives...
2020,2019
Sedan,SUV
100,500

You can simply do it by storing your desired array values in another array and then implode them or make them work by your need. Take a look at the following code..
$cars = [
'BMW' => [
'Year' => '2020',
'Body' => 'Sedan',
'Mileage' => '100'
],
'Ford' => [
'Year' => '2019',
'Body' => 'SUV',
'Mileage' => '500'
],
];
$year = [];
$body = [];
$milage = [];
foreach( $cars as $key => $car ){
if( $car['Year'] ){
$year[] = $car['Year'];
}
if( $car['Body'] ){
$body[] = $car['Body'];
}
if( $car['Mileage'] ){
$milage[] = $car['Mileage'];
}
}
var_dump(implode( ',', $year));
var_dump(implode( ',', $body));
var_dump(implode( ',', $milage));
will give you output:
string(9) "2020,2019" string(9) "Sedan,SUV" string(7) "100,500"

Related

Group rows of data by subarray in column and create subsets with variable depth

I have an array with two items, which are also arrays themselves: product and countries.
There are cases in which the countries array is the same for more than one product, like basic and pro in the example below.
Given this array:
$array = [
[
'product' => [
'value' => 'basic',
'label' => 'Basic'
],
'countries' => [
'Japan', // these
'Korea' // two...
],
],
[
'product' => [
'value' => 'pro',
'label' => 'Pro'
],
'countries' => [
'Japan', // ...and these two
'Korea' // are identical...
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
I would like to create new arrays grouped by countries, more precisely,
this is the result I'm after:
$array = [
[
'product' => [
[
'value' => 'basic',
'label' => 'Basic'
],
[
'value' => 'pro',
'label' => 'Pro'
]
],
'countries' => [
'Japan', // ...so they are now one single array
'Korea' // as the two products 'basic' and 'pro' have been grouped
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
As you can see in the second snippet, what I'm trying to do is to group basic and pro together in the same array, since they both share the exact same countries (Korea and Japan).
I've been trying for days to play around with this code, but it only seems to work if product and countries are strings rather than arrays:
$grouped = array();
foreach ($array as $element) {
$grouped[$element['countries']][] = $element;
}
var_dump($grouped);
This might be what you want
$productsByCountrySet = [];
foreach ($array as $product) {
$countries = $product['countries'];
sort($countries);
$countrySet = implode('/', $countries);
if (isset($productsByCountrySet[$countrySet])) {
$productsByCountrySet[$countrySet]['product'][] = $product['product'];
} else {
$productsByCountrySet[$countrySet] = [
'product' => [$product['product']],
'countries' => $countries,
];
}
}
$products = [];
foreach ($productsByCountrySet as $p) {
if (count($p['product']) == 1) {
$p['product'] = $p['product'][0];
}
$products[] = $p;
}
print_r($products);
It produces the output you're aiming for. It assumes that the order of countries is not significant (ie ['Japan', 'Korea'] is the same as ['Korea', 'Japan'])
It works by turning your countries array into a string (['Japan', 'Korea'] becomes 'Japan/Korea'), then uses that as a unique key for the entries. It builds up the desired output array by first assembling the unique key (I called it 'country set') and then checking if it has already been seen. If it has, the product is appended, if not, a new item is added to the output array.
The final section handles the case where there is only one product for a country set. We loop and catch this state, modifying the output accordingly.
I personally would not build the result structure that you are seeking because it would make the array processing code more convoluted, but hey, it's your project.
You need to establish consistent, first-level string keys in your result array so that you can determine if a set of countries has been encountered before.
If never encountered, save the full row data to the group.
If encountered, specifically, for a second time, you need to restructure the group's data (this is the elseif() logic).
If encountered more than twice, you can safely push the product's row data as a new child of the deeper structure.
Code: (Demo)
$result = [];
foreach ($array as $row) {
sort($row['countries']);
$compositeKey = implode('_', $row['countries']);
if (!isset($result[$compositeKey])) {
$result[$compositeKey] = $row;
} elseif (isset($result[$compositeKey]['product']['value'])) {
$result[$compositeKey]['product'] = [
$result[$compositeKey]['product'],
$row['product']
];
} else {
$result[$compositeKey]['product'][] = $row['product'];
}
}
echo json_encode(array_values($result), JSON_PRETTY_PRINT);
This general approach is efficient and direct because it only makes one pass over the array of data.
See my related answer: Group array row on one column and form subarrays of varying depth/structure
<?php
$array = [
[
'product' => [
'value' => 'basic',
'label' => 'Basic'
],
'countries' => [
'Japan', // these
'Korea' // two...
],
],
[
'product' => [
'value' => 'pro',
'label' => 'Pro'
],
'countries' => [
'Japan', // ...and these two
'Korea' // are identical...
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
// print(serialize($array));
$newarr = [];
//Here I am sorting the countries so that it can be compared and making a new array
foreach ($array as $key) {
$new = $key['countries'];
sort($key['countries']);
sort($key['product']);
$newarr[] = $key;
}
$result = [];
foreach($newarr as $key => $value) {
//Genetraing a unique key for each array type so that it can be compared
$ckey = md5(serialize($value['countries']));
$pkey = md5(serialize($value['product']));
//In the new array, the unique Countries key is used to generate a new array which will contain the product & countries
$result[$ckey]['product'][$pkey] = $value['product'];
//Product key is used to reduce redunant entires in product array
$result[$ckey]['countries'] = $value['countries'];
//This new loop is used to compare other arrays and group them together
foreach($newarr as $key2 => $value2) {
if($key != $key2 && $value['countries'] == $value2['countries']) {
$result[$ckey]['product'][$pkey] = $value2['product'];
}
}
}
print_r($result);
And the output is
Array
(
[00a9d5d0be04135916148f84706a2073] => Array
(
[product] => Array
(
[1c24c036cffc896aebf291da101ff88d] => Array
(
[0] => Pro
[1] => pro
)
[712ef34513bad5c6dd490337c22a5807] => Array
(
[0] => Basic
[1] => basic
)
)
[countries] => Array
(
[0] => Japan
[1] => Korea
)
)
[ae57f65be4cd65148d6f4ed3def12c8f] => Array
(
[product] => Array
(
[be5b95a64169e073ed0b6a72dfb79a83] => Array
(
[0] => Expert
[1] => expert
)
)
[countries] => Array
(
[0] => France
[1] => Japan
)
)
)
This way is little hacky and not the fastest solution but a working one. Since you don't have to do complex array operations it has unique keys rather than an index, that makes the process easy.l
Please read the code comments, I said how it works.
I have a solution in which I create a new array for the result called $newArray and I put the first element from $array into it. I then loop through each element in $array (except for the first one which I exclude using its key). For each element in $array, I loop through each element in $newArray. If both country names are present in $newArray, I just add the product array to $newArray. If there is no element in $newArray with both countries from the $array element being considered then I add the full $array element to $newArray. It does give your required array given your input array.
I had to change the way the product array appears in $newArray which explains the second and third lines of code below.
The & in &$subNewArr has the effect that $subNewArr is 'passed by reference' which means that it can be altered by the code where it is being used (see https://www.php.net/manual/en/language.types.array.php).
$newArray = [$array[0]];
$newArray[0]['product'] = [];
$newArray[0]['product'][] = $array[0]['product'];
foreach($array as $key => $subArr){
if($key > 0){
foreach($newArray as &$subNewArr){
if(
in_array($subArr['countries'][0], $subNewArr['countries']) &&
in_array($subArr['countries'][1], $subNewArr['countries'])
){
array_push($subNewArr['product'], $subArr['product']);
continue 2;
}
}
$newArray[] = $subArr;
}
}

Remove unwanted elements from subarrays in multidimensional array

I have a multidimensional array like this:
[
[
'id' => 1,
'name' => 'John',
'address' => 'Some address 1'
'city' => 'NY'
],
[
'id' => 2,
'name' => 'Jack',
'address' => 'Some address 2'
'city' => 'NY'
]
...
[ ... ]
]
How can I remove elements in all subarrays and only retain the id and name keys with their values?
Would this work?
$result = array_map(function($arr) {
return [
'id' => $arr['id'],
'name' => $arr['name']
];
}, $orig_array);
You want to retain the first two associative elements, so you can make array_slice() calls within array_map(). (Demo)
var_export(
array_map(fn($row) => array_slice($row, 0, 2), $array)
);
Or mapped called of array_intersect_key() against an establish whitelist array. (Demo)
$keep = ['id' => '', 'name' => ''];
var_export(
array_map(
fn($row) => array_intersect_key($row, $keep),
$array
)
)
Or, you could use array destructuring inside of a classic foreach() and make iterated compact() calls. (Demo)
$result = [];
foreach ($array as ['id' => $id, 'name' => $name]) {
$result[] = compact(['id', 'name']);
}
var_export($result);
If you want to edit the same array in place, you can simply iterate over them and unset them.
<?php
$preserve_keys = ['id','name'];
foreach($arr as &$data){
foreach($data as $key => $value){
if(!in_array($key,$preserve_keys)){
unset($data[$key]);
}
}
}
If you want it as a separate result, loop over and add it to the new array.
<?php
$new_result = [];
foreach($arr as $data){
$new_result[] = [
'id' => $data['id'],
'name' => $data['name']
];
}
print_r($new_result);

Add grouping elements to 2d array and sum values in respective group

I need to add new elemets to my array when a new category value is encountered. When a category value is encountered after the first time, its value1 and value2 values should be added to the first encounter's respective values.
Also, in the result array, I no longer wish to keep the category column. The category-grouping rows should use the category value as its name value.
Sample input:
$datas = [
[
'category' => 'Solution',
'name' => 'Name1',
'value1' => 20,
'value2' => 21
],
[
'category' => 'Solution',
'name' => 'Name2',
'value1' => 30,
'value2' => 31
],
[
'category' => 'Solution1',
'name' => 'Name3',
'value1' => 40,
'value2' => 41
]
];
Desired result:
[
['name' => 'Solution', 'value1' => 50, 'value2' => 52],
['name' => 'Name1', 'value1' => 20, 'value2' => 21],
['name' => 'Name2', 'value1' => 30, 'value2' => 31],
['name' => 'Solution1', 'value1' => 40, 'value2' => 41],
['name' => 'Name3', 'value1' => 40, 'value2' => 41]
]
I tried like this:
private function groupByProductSuperCategory($datas)
{
$return = [];
foreach ($datas as $data) {
$return[$data['category']][$data['name']] = array_sum(array_column('category', $data);
}
return $return;
}
The idea is to calculate first all sum values for by category, and after that just put values from name like another array. Have you an idea of how to do that?
From the posted array... To end in the desired array, there is some tiny fixes to do first. But I assumed it was due to typos while copying here...
So here is the array I started with:
$result = [
0 => [
"category" => 'Solution',
"name" => 'Name1',
"value1" => 20,
"value2" => 21
],
1 => [
"category" => 'Solution',
"name" => 'Name2',
"value1" => 30,
"value2" => 31
],
2 => [
"category" => 'Solution1',
"name" => 'Name3',
"value1" => 40,
"value2" => 41
]
];
Now, that re-organization of the data is a bit more complex than it looks... You need to perform several loops to:
Find distinct "category" names
Perform the summations for each
Add the sum item and the single items
So here is the code I ended with:
function groupByProductSuperCategory($datas){
$category = [];
$return = [];
// Find distinct categories
foreach ($datas as $data) {
if(!in_array($data["category"],$category)){
array_push($category,$data["category"]);
}
}
// For each distinct category, add the sum item and the single items
foreach ($category as $cat) {
// Get the sums
if(!in_array($cat,$return)){
$sum1 = 0;
$sum2 = 0;
foreach ($datas as $data) {
if($data["category"] == $cat){
$sum1 += $data["value1"];
$sum2 += $data["value2"];
}
}
}
// Push the sums in the return array
array_push($return,[
"name" => $cat,
"value1" => $sum1,
"value2" => $sum2,
]);
// Push the single elements
foreach ($datas as $data) {
if($cat == $data["category"]){
array_push($return,[
"name" => $data["name"],
"value1" => $data["value1"],
"value2" => $data["value2"],
]);
}
}
}
return $return;
}
Here is a PHPFiddle to try it out... Hit [F9] to run.
It is much more direct, efficient, and readable to implement a single loop and push reference variables into the result array to allow summing based on shared categories without keeping track of the actual indexes of the category rows.
Code: (Demo)
$result = [];
foreach ($array as $row) {
if (!isset($ref[$row['category']])) {
$ref[$row['category']] = [
'name' => $row['category'],
'value1' => 0,
'value2' => 0
];
$result[] = &$ref[$row['category']];
}
$ref[$row['category']]['value1'] += $row['value1'];
$ref[$row['category']]['value2'] += $row['value2'];
unset($row['category']);
$result[] = $row;
}
var_export($result);

Array manipulation (compare) by specific algortihm

I have 2 array and i need compare these arrays by my specific algorithm.
Firstly, my arrays:
$old = [
'pencil' => 'red',
'eraser' => 'green',
'bag' => 'blue'
];
$new = [
'pencil' => '',
'eraser' => '',
'computer' => 'mac',
'bag' => '',
'activity' => [
'jumping',
'pool',
'reading'
]
];
Then, I wanna get this output:
$output = [
'pencil' => 'red', // old value
'eraser' => 'green', // old value
'bag' => 'blue', // old value
'computer' => 'mac', // new key & values
'activity' => [ // new key & values
'jumping',
'pool',
'reading'
]
];
So, elements (array item) in both old and new arrays will be added to the output but values should come from the old array.
The elements (array item) in the new array should be transferred to output exactly.
I wanna support my question with a photo attachment ( the sequence on the photo may not match the sequence on the my arrays ($old, $new) ):
photo
Use array_merge in order to merge element of two array:
$result = array_merge($new, $old);
The values from the second array ($old) will be merged on the first array so if you have a key in both array, the second one will be presented in the result.
I think the following code can achieve what you are looking for:
$output = []
foreach($old as $key => $value){
$output[$key] = $value;
}
foreach($new as $key => $value){
if(!array_key_exists($key, $output)){
$output[$key] = $value;
}
}
Here is my solution,
$old = [
'pencil' => 'red',
'eraser' => 'green',
'bag' => 'blue'
];
$new = [
'pencil' => '',
'eraser' => '',
'computer' => 'mac',
'bag' => '',
'activity' => [
'jumping',
'pool',
'reading'
]
];
$output = [];
foreach ($new as $newkey => $newvalue) {
if($newvalue!=""){
$output = [$old+$new];
}
}
echo "<pre>";
print_r($output);
echo "</pre>";
exit;
Here output look like,
Array
(
[0] => Array
(
[pencil] => red
[eraser] => green
[bag] => blue
[computer] => mac
[activity] => Array
(
[0] => jumping
[1] => pool
[2] => reading
)
)
)

push certain elements into array php

I want to know that is there a way to insert certain elements of an array into a new array. I mean I have an array containing 10 objects. Each object has 3 or four fields for example id, name , age , username. now I want to insert the id's of all the objects into the new array with a single call.Is there anyway to do that.
$array = [
[0] => [
id =>
name =>
],
[1] = > [
id =>
name =>
]
]
and so on now I want to insert all the id's of all the object into a new array with a single call. Is there a way to do that?
Use array_map() function.
Here is your solution:-
$ids = array_map( function( $arr ){
return $arr["id"];
}, $arr );
echo '<pre>'; print_r($ids);
A basic foreach loop will do the job just fine
$firstArray = array(
array(
'id' => 1,
'name' => 'abc'
),
array(
'id' => 2,
'name' => 'def'
),
array(
'id' => 3,
'name' => 'gh'
)
);
$onlyIds = array();
$onlyKeys = array();
//To get the array value 'id'
foreach($firstArray as $value){
$onlyIds[] = $value['id'];
}
//To get the array keys
foreach($firstArray as $key => $value){
$onlyKeys[] = $key;
}
You could use array_walk which could be considered a "single call"
$array = array(0 => array('id', 'name', 'age'), 1 => array('id', 'name', 'age'));
array_walk($array, function($item, $key) {
// $key is 0 or 1
// $item is either id, name, age
});
You can use array_column.
$arr = [ ['id' => 1, 'username' => 'a'], ['id' => 2, 'username' => 'b'] ];
$ids = array_column($arr, 'id')
$ids == [1, 2]

Categories