PHP: Link address of multiple array keys into another bigger array - php

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

Related

How to merge array using UUID value

I have the following code :
<?php
$arr1 = array();
$arr1[] = ['UUID' => '123a-123a', 'name' => 'A1'];
$arr1[] = ['UUID' => '123b-123b', 'name' => 'B1'];
$arr1[] = ['UUID' => '123c-123c', 'name' => 'C1'];
$arr2 = array();
$arr2[] = ['UUID' => '123a-123a', 'name' => 'A2'];
$arr2[] = ['UUID' => '123b-123b', 'name' => 'B2'];
$arr2[] = ['UUID' => '123c-123c', 'name' => 'C2'];
$new_arr1 = array();
foreach ($arr1 as $key => $value) {
if(isset($new_arr1[$value['UUID']])){
$new_arr1[$value['UUID']] += ['name_a' => $value['name']];
}else{
$new_arr1[$value['UUID']] = ['name_a' => $value['name']];
}
}
$new_arr2 = array();
foreach ($arr2 as $key => $value) {
if(isset($new_arr2[$value['UUID']])){
$new_arr2[$value['UUID']] += ['name_1' => $value['name']];
}else{
$new_arr2[$value['UUID']] = ['name_2' => $value['name']];
}
}
$final_array = array_combine($new_arr1, $new_arr2);
var_dump($final_array);
Which give me the following error :
Warning: Array to string conversion in /home/user/scripts/code.php on line 32
Snippet :
https://sandbox.onlinephpfunctions.com/c/cf5fd
I want to use the UUID as an array id.
here is the expected output :
Array
(
[123a-123a] => Array
(
[name_1] => A1
[name_2] => A2
)
[123b-123b] => Array
(
[name_1] => B1
[name_2] => B2
)
[123c-123c] => Array
(
[name_1] => C1
[name_2] => C2
)
)
Instead of use array_combine you need to use array_merge_recursive because is multidimensional
Snippet:
https://sandbox.onlinephpfunctions.com/c/eae35
Reference:
array_merge_recursive
No array_combine nor intermediate arrays needed, just construct new array from those you already have:
$arr1 = array();
$arr1[] = ['UUID' => '123a-123a', 'name' => 'A1'];
$arr1[] = ['UUID' => '123b-123b', 'name' => 'B1'];
$arr1[] = ['UUID' => '123c-123c', 'name' => 'C1'];
$arr2 = array();
$arr2[] = ['UUID' => '123a-123a', 'name' => 'A2'];
$arr2[] = ['UUID' => '123b-123b', 'name' => 'B2'];
$arr2[] = ['UUID' => '123c-123c', 'name' => 'C2'];
$final_array = [];
foreach( array_merge($arr1, $arr2) as $entry ){
if( empty( $final_array[$entry['UUID']] ) )
$final_array[$entry['UUID']] = ['name_1' => $entry['name']];
else
$final_array[$entry['UUID']][ 'name_' . (count( $final_array[$entry['UUID']] ) + 1) ] = $entry['name'];
}
print_r($final_array);
array_combine creates an array by using one array for keys and another for its values. In your case, each of the 2 arrays have individual subarrays in them. Hence the error when it tries to make one of the subarrays as a string(which it can't).
In your code, you can use a single $new_arr and the make the code more concise with the null coalescing operator(??) like below:
<?php
$new_arr = array();
foreach (array_merge($arr1,$arr2) as $key => $value) {
$new_arr[$value['UUID']] = $new_arr[$value['UUID']] ?? [];
$new_arr[$value['UUID']] += [('name_' . (count($new_arr[$value['UUID']]) + 1)) => $value['name']];
}
print_r($new_arr);
Online Demo

Get all values from multidimensional array

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

Using PHP array iterator to edit array

I have a multidimensional array with variable number of levels of data. That is, I can't be sure how many iterations it will take to reach the Goal level, which is an array. Something like this:
[
'key1' = 'value1',
'key2' = 'value2',
'key3' = [
'key4' => [
'key5' => 'value3'
],
'key6' => 'value4'
],
'key7' => [
'Goal' => [
'value5',
'value6',
'value7'
]
],
'key8' => 'value8'],
'key9' => [
'Goal' => [
'value9',
'Foo',
'value10'
]
]
]
I've tried both array_walk_recursive and ArrayIterator, but neither seems to quite get me where I need to be.
I need to go through each element of the array, and if the key is Goal examine the value (eg. the array that Goal holds) and see if that array contains the value Foo.
If Foo is found in the array, I need to add a new value (in addition to Foo-- so call it Bar) to the array and then continue, since there may be more Goals in the parent array.
Is there a way to "stop" the iterator when we find a Goal, without iterating further, and then do the array_search operation?
Edit: Trying somethings along these lines--
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($iterator as $key => $value)
{
if($key == 'Goal')
{
if (is_array($value)) {
if(array_search('Foo', $value)) {
$value[] = 'Bar';
}
}
}
}
Not entirely sure if this is what you want to achieve but here's a solution which adds Bar to arrays nested in the Goal key:
$array = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => [
'key4' => [
'key5' => 'value3',
],
'key6' => 'value4',
],
'key7' => [
'Goal' => [
'value5',
'value6',
'value7',
],
],
'key8' => 'value8',
'key9' => [
'Goal' => [
'value9',
'Foo',
'value10',
],
],
];
function iterate(array $data, $goal = false)
{
foreach ($data as $key => &$value) {
if (is_array($value)) {
$value = iterate($value, $key === 'Goal');
} elseif (is_string($value)) {
if (($value === 'Foo') && $goal) {
$data[] = 'Bar';
return $data;
}
}
}
return $data;
}
var_export(iterate($array));
The code generates the following output:
array (
'key1' => 'value1',
'key2' => 'value2',
'key3' =>
array (
'key4' =>
array (
'key5' => 'value3',
),
'key6' => 'value4',
),
'key7' =>
array (
'Goal' =>
array (
0 => 'value5',
1 => 'value6',
2 => 'value7',
),
),
'key8' => 'value8',
'key9' =>
array (
'Goal' =>
array (
0 => 'value9',
1 => 'Foo',
2 => 'value10',
3 => 'Bar',
),
),
)
Iterators in my opinion would be weird to use in these kind of arrays... I would do it with something like this:
/*
Usage:
$wasFound = checkArray( "Goal", "Foo", $the_array);
if ( $wasFound ) echo "Key and Value pair found in the array!";
else { /* not found */ }
*/
function checkArray( $key_to_find, $value_to_find, $my_var, $last_key = NULL ) {
$found = FALSE;
if ( $last_key == $key_to_find && $my_var == $value_to_find )
return TRUE;
if ( $my_var == NULL )
return FALSE;
if ( is_array( $my_var ) ) {
foreach ( $my_var AS $key => $value )
{
if ( $found ) {
/* Do something else if needed when found */
break;
}
$found = checkArray( $key_to_find, $value_to_find, $value, $key );
}
}
return $found;
}
I agree that recursion is the way to do this. The problem with using array_walk_recursive is that you will not be able to see the Goal key, because as per the PHP documentation,
Any key that holds an array will not be passed to the function.
I am not really sure whether using a RecursiveIteratorIterator would be better than just writing a recursive function for it. A function for something like this should be fairly simple.
function addBar(&$array) {
foreach ($array as $key => &$value) {
if ($key === 'Goal') {
if(array_search('Foo', $value)) {
$value[] = 'Bar';
}
} elseif (is_array($value)) {
addBar($value);
}
}
}
This function takes a reference to your array, so it will update your actual array rather than creating a copy with Bar added to each Goal.

PHP : multidimensional array merge recursive

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

PHP Recursively unset array keys if match

I have the following array that I need to recursively loop through and remove any child arrays that have the key 'fields'. I have tried array filter but I am having trouble getting any of it to work.
$myarray = array(
'Item' => array(
'fields' => array('id', 'name'),
'Part' => array(
'fields' => array('part_number', 'part_name')
)
),
'Owner' => array(
'fields' => array('id', 'name', 'active'),
'Company' => array(
'fields' => array('id', 'name',),
'Locations' => array(
'fields' => array('id', 'name', 'address', 'zip'),
'State' => array(
'fields' => array('id', 'name')
)
)
)
)
);
This is how I need it the result to look like:
$myarray = array(
'Item' => array(
'Part' => array(
)
),
'Owner' => array(
'Company' => array(
'Locations' => array(
'State' => array(
)
)
)
)
);
If you want to operate recursively, you need to pass the array as a reference, otherwise you do a lot of unnecessarily copying:
function recursive_unset(&$array, $unwanted_key) {
unset($array[$unwanted_key]);
foreach ($array as &$value) {
if (is_array($value)) {
recursive_unset($value, $unwanted_key);
}
}
}
you want array_walk
function remove_key(&$a) {
if(is_array($a)) {
unset($a['fields']);
array_walk($a, __FUNCTION__);
}
}
remove_key($myarray);
My suggestion:
function removeKey(&$array, $key)
{
if (is_array($array))
{
if (isset($array[$key]))
{
unset($array[$key]);
}
if (count($array) > 0)
{
foreach ($array as $k => $arr)
{
removeKey($array[$k], $key);
}
}
}
}
removeKey($myarray, 'Part');
function recursive_unset(&$array, $unwanted_key) {
if (!is_array($array) || empty($unwanted_key))
return false;
unset($array[$unwanted_key]);
foreach ($array as &$value) {
if (is_array($value)) {
recursive_unset($value, $unwanted_key);
}
}
}
function sanitize($arr) {
if (is_array($arr)) {
$out = array();
foreach ($arr as $key => $val) {
if ($key != 'fields') {
$out[$key] = sanitize($val);
}
}
} else {
return $arr;
}
return $out;
}
$myarray = sanitize($myarray);
Result:
array (
'Item' =>
array (
'Part' =>
array (
),
),
'Owner' =>
array (
'Company' =>
array (
'Locations' =>
array (
'State' =>
array (
),
),
),
),
)
function removeRecursive($haystack,$needle){
if(is_array($haystack)) {
unset($haystack[$needle]);
foreach ($haystack as $k=>$value) {
$haystack[$k] = removeRecursive($value,$needle);
}
}
return $haystack;
}
$new = removeRecursive($old,'key');
Code:
$sweet = array('a' => 'apple', 'b' => 'banana');
$fruits = array('sweet' => $sweet, 'sour' => $sweet);
function recursive_array_except(&$array, $except)
{
foreach($array as $key => $value){
if(in_array($key, $except, true)){
unset($array[$key]);
}else{
if(is_array($value)){
recursive_array_except($array[$key], $except);
}
}
}
return;
}
recursive_array_except($fruits, array('a'));
print_r($fruits);
Input:
Array
(
[sweet] => Array
(
[a] => apple
[b] => banana
)
[sour] => Array
(
[a] => apple
[b] => banana
)
)
Output:
Array
(
[sweet] => Array
(
[b] => banana
)
[sour] => Array
(
[b] => banana
)
)
I come up with a simple function that you can use to delete multiple array element based on multiple keys.
Detail example here.
Just a little change in code.
function removeRecursive($inputArray,$delKey){
if(is_array($inputArray)){
$moreKey = explode(",",$delKey);
foreach($moreKey as $nKey){
unset($inputArray[$nKey]);
foreach($inputArray as $k=>$value) {
$inputArray[$k] = removeRecursive($value,$nKey);
}
}
}
return $inputArray;
}
$inputNew = removeRecursive($input,'keyOne,keyTwo');
print"<pre>";
print_r($inputNew);
print"</pre>";
Give this function a shot. It will remove the keys with 'fields' and leave the rest of the array.
function unsetFields($myarray) {
if (isset($myarray['fields']))
unset($myarray['fields']);
foreach ($myarray as $key => $value)
$myarray[$key] = unsetFields($value);
return $myarray;
}
Recursively walk the array (by reference) and unset the relevant keys.
clear_fields($myarray);
print_r($myarray);
function clear_fields(&$parent) {
unset($parent['fields']);
foreach ($parent as $k => &$v) {
if (is_array($v)) {
clear_fields($v);
}
}
}
I needed to have a little more granularity in unsetting arrays and I came up with this - with the evil eval and other dirty tricks.
$post = array(); //some huge array
function array_unset(&$arr,$path){
$str = 'unset($arr[\''.implode('\'][\'',explode('/', $path)).'\']);';
eval($str);
}
$junk = array();
$junk[] = 'property_meta/_edit_lock';
$junk[] = 'property_terms/post_tag';
$junk[] = 'property_terms/property-type/0/term_id';
foreach($junk as $path){
array_unset($post,$path);
}
// unset($arr['property_meta']['_edit_lock']);
// unset($arr['property_terms']['post_tag']);
// unset($arr['property_terms']['property-type']['0']['term_id']);

Categories