Let's say, I have an array like this:
$array = [
'car' => [
'BMW' => 'blue',
'toyota' => 'gray'
],
'animal' => [
'cat' => 'orange',
'horse' => 'white'
]
];
Then, I want to get all the values (the colour, 'blue', 'gray', 'orange', 'white') and join them into a single array. How do I do that without using foreach twice?
Thanks in advance.
TL;DR
$result = call_user_func_array('array_merge', $array);
Credit: How to "flatten" a multi-dimensional array to simple one in PHP?
In your use case, you should use it like this:
<?php
$array = [
'car' => [
'BMW' => 'blue',
'toyota' => 'gray'
],
'animal' => [
'cat' => 'orange',
'horse' => 'white'
]
];
$result = call_user_func_array('array_merge', $array);
$result = array_values($result);
//$result = ['blue', 'gray', 'orange', 'white']
Old but as far i see not really a "working on all cases" function posted.
So here is the common classic recursively function:
function getArrayValuesRecursively(array $array)
{
$values = [];
foreach ($array as $value) {
if (is_array($value)) {
$values = array_merge($values,
getArrayValuesRecursively($value));
} else {
$values[] = $value;
}
}
return $values;
}
Example array:
$array = [
'car' => [
'BMW' => 'blue',
'toyota' => 'gray'
],
'animal' => [
'cat' => 'orange',
'horse' => 'white'
],
'foo' => [
'bar',
'baz' => [
1,
2 => [
2.1,
'deep' => [
'red'
],
2.2,
],
3,
]
],
];
Call:
echo var_export(getArrayValuesRecursively($array), true) . PHP_EOL;
Result:
// array(
// 0 => 'blue',
// 1 => 'gray',
// 2 => 'orange',
// 3 => 'white',
// 4 => 'bar',
// 5 => 1,
// 6 => 2.1,
// 7 => 'red',
// 8 => 2.2,
// 9 => 3,
// )
Try this:
function get_values($array){
foreach($array as $key => $value){
if(is_array($array[$key])){
print_r (array_values($array[$key]));
}
}
}
get_values($array);
How do I do that without using foreach twice?
First use RecursiveIteratorIterator class to flatten the multidimensional array, and then apply array_values() function to get the desired color values in a single array.
Here are the references:
RecursiveIteratorIterator class
array_values()
So your code should be like this:
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$flatten_array = array_values(iterator_to_array($iterator,true));
// display $flatten_array
echo "<pre>"; print_r($flatten_array);
Here's the live demo
Here's a recursive function that gives you both the ability to get an array of those endpoint values, or to get an array with all keys intact, but just flattened.
Code:
<?php
$array = [
'car' => [
'BMW' => 'blue',
'toyota' => 'gray'
],
'animal' => [
'cat' => 'orange',
'horse' => 'white'
]
];
//
print "\n<br> Array (Original): ".print_r($array,true);
print "\n<br> Array (Flattened, With Keys): ".print_r(FlattenMultiArray($array,true),true);
print "\n<br> Array (Flattened, No Keys): ".print_r(FlattenMultiArray($array,false),true);
//
function FlattenMultiArray($array,$bKeepKeys=true,$key_prefix='')
{
//
$array_flattened=Array();
//
foreach($array as $key=>$value){
//
if(Is_Array($value)){
$array_flattened=Array_Merge(
$array_flattened,
FlattenMultiArray($value,$bKeepKeys,$key)
);
}
else{
if($bKeepKeys){
$array_flattened["{$key_prefix}_{$key}"]=$value;
}
else{
$array_flattened[]=$value;
}
}
}
return $array_flattened;
}
?>
Outputs:
<br> Array (Original): Array
(
[car] => Array
(
[BMW] => blue
[toyota] => gray
)
[animal] => Array
(
[cat] => orange
[horse] => white
)
)
<br> Array (Flattened, With Keys): Array
(
[car_BMW] => blue
[car_toyota] => gray
[animal_cat] => orange
[animal_horse] => white
)
<br> Array (Flattened, No Keys): Array
(
[0] => blue
[1] => gray
[2] => orange
[3] => white
)
If you don't care about the indexes, then this should do it:
$colors = array();
foreach ($array as $item) {
$colors = array_merge($colors, array_values($item));
}
If you want to keep the indexes you could use:
$colors = array();
foreach ($array as $item) {
// this leaves you open to elements overwriting each other depending on their keys
$colors = array_merge($colors, $item);
}
I hope this helps.
what about this one?It works with ANY array depth.
private function flattenMultiArray($array)
{
$result = [];
self::flattenKeyRecursively($array, $result, '');
return $result;
}
private static function flattenKeyRecursively($array, &$result, $parentKey)
{
foreach ($array as $key => $value) {
$itemKey = ($parentKey ? $parentKey . '.' : '') . $key;
if (is_array($value)) {
self::flattenKeyRecursively($value, $result, $itemKey);
} else {
$result[$itemKey] = $value;
}
}
}
P.S. solution is not mine, but works perfectly, and I hope it will help someone.
Original source:
https://github.com/letsdrink/ouzo/blob/master/src/Ouzo/Goodies/Utilities/Arrays.php
Related
I have array name $main_array
$main_array = [
[
'product_id' => '1',
'values' => '1"'
],
[
'product_id' => '4',
'values' => '1"'
],
[
'product_id' => '4',
'values' => 'blue'
],
[
'product_id' => '5',
'values' => 'blue'
]
];
I want to check values from other array
$check_array = [
'1"','blue'
];
Find product_id where 1" && blue both matching
Expected output ::
$output = [
[
'product_id' => '4',
'values' => '1"'
],
[
'product_id' => '4',
'values' => 'blue'
]
];
You could use an array to store matching elements using the product id as key.
$include = [] ;
foreach ($main_array as $key => $item) {
// if values match to $check_array
if (in_array($item['values'], $check_array)) {
// store using product id as key
$pid = $item['product_id'] ;
$include[$pid][] = $key;
}
}
// Filter to keep only items that match with all conditions
$include = array_filter($include, function($a) use ($check_array) {
return count($a) == count($check_array) ;
}) ;
$include = reset($include) ; // Get the first
// Recreate final array :
$out = [] ;
foreach ($include as $elem) {
$out[] = $main_array[$elem] ;
}
print_r($out);
Will outputs :
Array
(
[0] => Array
(
[product_id] => 4
[values] => 1"
)
[1] => Array
(
[product_id] => 4
[values] => blue
)
)
You may use nested foreach cycles. Supposed that $check_array containes two fields, you can write a code like this:
$output = array();
$match_array = array();
//check the first field correspondence
foreach ($main_array as $key1=>$sub_main) {
foreach ($sub_main as $key2=>$item) {
if ($check_array[0] == $item['values']) {
$match_array[] = $item['product_id'];
}
}
}
//try to match the second field
foreach ($main_array as $key1=>$sub_main) {
foreach ($sub_main as $key2=>$item) {
if ($check_array[1] == $item['values']) {
if (in_array($item['product_id'], $match_array) {
$output[] = array($item['product_id'], $check_array[1]);
$output[] = array($item['product_id'], $check_array[2]);
}
}
}
}
So lets say I have a multidimentional array as such
$myArray = [
0 = [
fruit = 'apple',
juice = 'orange',
cars = [bmw = 'blue', audi = 'red', ford = 'yellow']
],
1 = [
fruit = 'strawberry',
juice = 'grape',
cars = [bmw = 'grey', mazda = 'blue', hummer = 'orange']
],
]
and some replacement array values for cars
$replaceCarsArray = [ferrari = 'red', lamborghini = 'blue, masarati = 'pink']
I foreach through the array with $key => &$values (value being passed by reference)
foreach ($myArray as $key => &$values) {
foreach ($values as $key2 => &$value) {
if ($key2 == 'cars'){
$value = $replaceCarsArray;
}
}
}
that works and replaces the entire cars values with the $replaceCarsArray
but what if I want to target one of the items in that cars array and change the color? So this is what I tried:
foreach ($myArray as $key => &$values) {
foreach ($values as $key2 => &$value) {
if ($key2 == 'cars' && $value['bmw'] != 'red'){
$value['bmw'] = 'red';
}
}
}
this however does not seem to work and the bmw color does not get updated to red. How would I be able to change that data?
Note this is example data and I wrote it very quickly for all intents and purposes I do have access to all of the values and I do not have any syntactical errors in my code as might have appeared here.
Try the following
$myArray[$key][$key2]['bmw'] = 'blue';
That worked for me
<?php
$myArray = [
0 => [
'fruit' => 'apple',
'juice' => 'orange',
'cars' => ['bmw' => 'blue', 'audi' => 'red', 'ford' => 'yellow']
],
1 => [
'fruit' => 'strawberry',
'juice' => 'grape',
'cars' => ['bmw' => 'grey', 'mazda' => 'blue', 'hummer' => 'orange']
]
];
echo "<pre>";
var_dump($myArray);
echo "</pre>";
foreach ($myArray as $key => &$values) {
foreach ($values as $key2 => &$value) {
if ($key2 == 'cars' && $value['bmw'] != 'red'){
$value['bmw'] = 'red';
}
}
}
echo "<pre>";
var_dump($myArray);
echo "</pre>";
echo $myArray[0]['cars']['ford'];
echo $myArray[0]['cars']['bmw'];
tested on : http://phptester.net/
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
)
)
)
I want to combine 3 small arrays that have unique keys between them into 1 big array but when I modify a value in the big array I want it also to reflect in the corresponding small array.
For example I have these 3 small arrays:
$arr1 = ['key1' => 'data1', 'key2' => 'data2'];
$arr2 = ['key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5'];
$arr3 = ['key6' => 'data6'];
I want to have a $bigArray that has each key's address linked/mapped to each value of the small arrays. So if I do something like:
$bigArray['key4'] = 'something else';
then it would modify $arr2['key4'] to the same value ('something else').
If I try something like:
$bigArray = [&$arr1, &$arr2, &$arr3];
It has the unfortunate effect of making a multidimensional array with the keys to the values mapped.
Two ways i found
<?php
error_reporting(E_ALL);
$arr1 = ['key1' => 'data1', 'key2' => 'data2'];
$arr2 = ['key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5'];
$arr3 = ['key6' => 'data6'];
$big = [];
if (true) {
foreach (['arr1', 'arr2', 'arr3'] as $v) {
foreach (array_keys($$v) as $k) {
$big[$k] = &${$v}[$k];
}
}
}
else {
foreach ([&$arr1, &$arr2, &$arr3] as &$v) {
foreach (array_keys($v) as $k) {
$big[$k] = &$v[$k];
}
}
}
$big['key1'] = 'data1mod';
print_r($big);
print_r($arr1);
3rd way with function
$big = [];
$bindToBig = function (&$a) use (&$big) {
foreach (array_keys($a) as $k) {
$big[$k] = &$a[$k];
}
};
$bindToBig($arr1);
$bindToBig($arr2);
$bindToBig($arr3);
You can't bind data that way, but you can link them in the same object:
class ArrayLink {
public $bigArray;
public $linkedChildrenArray;
protected $childrenArray;
public function __construct( $childrenArray ) {
$this->childrenArray = $childrenArray;
}
public function changeValueForKey( $arrKey, $arrValue ) {
foreach ( $this->childrenArray as $key => $value ) {
foreach ( $value as $subKey => $subValue ) {
if ( $arrKey == $subKey ) {
$this->bigArray[ $subKey ] = $arrValue;
$this->childrenArray[ $key ][ $subKey ] = $arrValue;
}
}
}
$this->linkedChildrenArray = (object) $this->childrenArray;
}
}
As you can see, the $arr2 now need to be access from $arrayLink object:
$arr1 = [ 'key1' => 'data1', 'key2' => 'data2' ];
$arr2 = [ 'key3' => 'data3', 'key4' => 'data4', 'key5' => 'data5' ];
$arr3 = [ 'key6' => 'data6' ];
$arrayLink = new ArrayLink( array( 'arr1' => $arr1, 'arr2' => $arr2, 'arr3' => $arr3 ) );
$arrayLink->changeValueForKey( 'key3', 'new value for key 3' );
echo $arrayLink->bigArray['key3']; //new value for key 3
echo $arrayLink->linkedChildrenArray->arr2['key3']; //new value for key 3
I need to merge those two arrays:
$ar1 = array("color" => array("red", "green"), "aa");
$ar2 = array("color" => array( "green", "blue"), "bb");
$result = array_merge_recursive($ar1, $ar2);
Expected output:
[
'color' => [
(int) 0 => 'red',
(int) 1 => 'green',
(int) 3 => 'blue'
],
(int) 0 => 'aa',
(int) 1 => 'bb'
]
But it outputs:
[
'color' => [
(int) 0 => 'red',
(int) 1 => 'green',
(int) 2 => 'green', (!)
(int) 3 => 'blue'
],
(int) 0 => 'aa',
(int) 1 => 'bb'
]
I'm looking for the simplest way to do this, my array inputs won't be deeper than those examples.
Here it is.
function array_merge_recursive_ex(array $array1, array $array2)
{
$merged = $array1;
foreach ($array2 as $key => & $value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = array_merge_recursive_ex($merged[$key], $value);
} else if (is_numeric($key)) {
if (!in_array($value, $merged)) {
$merged[] = $value;
}
} else {
$merged[$key] = $value;
}
}
return $merged;
}
Thanks to Meglio comment, you can use these functions for any number of arrays :
Functions
function drupal_array_merge_deep() {
$args = func_get_args();
return drupal_array_merge_deep_array($args);
}
// source : https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_array_merge_deep_array/7.x
function drupal_array_merge_deep_array($arrays) {
$result = array();
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
// Renumber integer keys as array_merge_recursive() does. Note that PHP
// automatically converts array keys that are integer strings (e.g., '1')
// to integers.
if (is_integer($key)) {
$result[] = $value;
}
elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
$result[$key] = drupal_array_merge_deep_array(array(
$result[$key],
$value,
));
}
else {
$result[$key] = $value;
}
}
}
return $result;
}
Usage
$merged = drupal_array_merge_deep($ar_1, $ar_2);
var_dump($merged);
$merged = drupal_array_merge_deep_array([$ar_1, $ar_2]);
var_dump($merged);
Usage (test data)
$ar_1 = [
"item1" => false,
"item2" => true,
"item_list" => [
"sub_item1" => 5,
"sub_itemlist" => [
"sub_sub_item1" => 27,
],
]
];
$ar_2 = [
"item_list" => [
"sub_item2" => 5,
"sub_itemlist" => [
"sub_sub_item2" => 27,
],
],
"item3" => true,
];
Usage output (same for both functions)
array (size=4)
'item1' => boolean false
'item2' => boolean true
'item_list' =>
array (size=3)
'sub_item1' => int 5
'sub_itemlist' =>
array (size=2)
'sub_sub_item1' => int 27
'sub_sub_item2' => int 27
'sub_item2' => int 5
'item3' => boolean true