Reset array keys in multidimensional array, recursive, by reference - php

I want to reset keys in a big, multidimensional array. I already found a solution which is actually work:
$fix_keys = function(array $array) use (&$fix_keys)
{
foreach($array as $k => $val)
{
if (is_array($val))
{
$array[$k] = $fix_keys($val);
}
}
return array_values($array);
};
and the problem is, if I pass big arrays to it, it becomes slow and memory consuming. What about refactoring with working references:
$fix_keys = function(array &$array) use (&$fix_keys)
{
foreach($array as $k => &$val)
{
if (is_array($val))
{
$array[$k] = $fix_keys($val);
}
}
unset($val);
$array = array_values($array);
};
but it messed up the array, all I get is [0] => null. What is wrong?
Edit: so input data:
$a = [
'a' => 1,
'b' => 2,
'c' => [
'aa' => 11,
'bb' => [
'ccc' => 1
],
'cc' => 33
]
];
and I want to have:
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
array(3) {
[0]=>
int(11)
[1]=>
array(1) {
[0]=>
int(1)
}
[2]=>
int(33)
}
}

If memory is an issue you can try using yield. I'm not sure if this fits your needs, but here it is:
function reduce($array){
foreach($array as $key => $value){
if(is_array($value)){
reduce($value);
}
}
yield array_values($array);
}
You can also use send if you need to apply some logic to the generator.

I found the solution:
$fix_keys = function(array &$array) use (&$fix_keys)
{
foreach(array_keys($array) as $k)
{
if (is_array($array[$k]))
{
$fix_keys($array[$k]);
}
}
$array = array_values($array);
};

Related

Merge associative arrays by partial key match

I have an array:
$a = [
"g41" => 1,
"g44" => 2,
"g53" => 3
];
And another array is:
$list = [
40,
41,
44,
46,
53
];
How to combine these arrays by partial key matches to produce the following results?
$result = [
"41" => 1,
"44" => 2,
"53" => 3,
"40" => null,
"46" => null
];
Iterate $list array and check for the key in the other $a array:
$a = array("g41" => 1, "g44" => 2, "g53" => 3);
$list = array(40, 41, 44, 46, 53);
$result = [];
foreach ($list as $key) {
$result[$key] = $a["g$key"] ?? null;
}
var_dump($result);
Output:
array(5) {
[40]=> NULL
[41]=> int(1)
[44]=> int(2)
[46]=> NULL
[53]=> int(3)
}
since you want to the result sequence follow $a, you may need to unset $list, and push remaining lists to $result
sample code as below:
<?php
$a=["g41"=>1,"g44"=>2,"g53"=>3];
$list=[40,41,44,46,53];
$result =[];
foreach($a as $key => $value){
$new_key = substr($key,1); //remove first character, if only you first key always single char, else you may use your own method to extract key
if(in_array($new_key,$list)){
$result[$new_key] = $value;
unset($list[array_search($new_key, $list)]);//remove used key
}
}
//push remaining as null
foreach($list as $v){
$result[$v] = null;
}
print_r($result);
write your custom script
$result = [];
foreach($a as $key => $value) $result[trim($key, 'g')] = $value;
foreach($list as $key) if (!isset($result[strval($key)]) $result[strval($key)] = null;
Loop the $list array
Check if the list value (prepended with g) exists in the $a array. If so, push it with the desired numeric key into the result array; if not, push it into an alternative array with the desired numeric key and the default value of null.
When finished iterating, append the data from the alternative array to the result array.
This will properly filter your data, apply the expected default values and sort the output exactly as requested.
Code: (Demo)
foreach ($list as $v) {
if (isset($a["g$v"])) {
$result[$v] = $a["g$v"];
} else {
$extra[$v] = null;
}
}
var_export(
array_replace($result ?? [], $extra ?? [])
);
I am using ?? [] to fallback to an empty array in case either of the arrays are never declared (never receive any elements).
It looks like what you want to do first is transform the keys in array $a. Then use your $list of keys to populate null values where none are present in $a.
$aKeys = preg_replace('/\D/', '', array_keys($a));
$a = array_combine($aKeys, $a);
$defaultList = array_fill_keys($list, null);
$list = array_merge($defaultList, $a);

Codeigniter - merge multiple associative array with condition

I need to merge multiple arrays into one where a specific key & its value are same. Here is the Sample_Array1
array(n) {
[0]=> array {
["a"]=> "m1"
["b"]=> "x2"
}
[1]=> array {
["a"]=> "n1"
["b"]=> "y2"
} ....
Sample_Array2 with one common key & other different ones.
array(n) {
[0]=> array {
["b"]=> "x2"
["c"]=> "p1"
}
[1]=> array {
["b"]=> "x2"
["d"]=> "q1"
}
[2]=> array {
["b"]=> "y2"
["e"]=> "r1"
} ....
Need to merge / append Sample_Array2 to Sample_Array1 where key-"b" & its value are same. The expected output:
array(n) {
[0]=>
array(2) {
["a"]=> "m1"
["b"]=> "x2"
["c"]=> "p1"
["d"]=> "q1"
}
[1]=>
array(2) {
["a"]=> "n1"
["b"]=> "y2"
["e"]=> "r1"
} ....
I have tried so many similar questions but couldn't find the exact result.
PHP merge arrays with a condition The answer given on this link is not solving the purpose, its making different array for each new key, while I need to append the new keys in one array.
This should work, assuming you have the "b" index in all sub arrays.
$array1 = array();
$array1[] = array("a" => "m1", "b" => "x2", "c" => null);
$array1[] = array("a" => "n1", "b" => "y2");
$array2 = array();
$array2[] = array("b" => "x2", "c" => "p1");
$array2[] = array("a" => null, "b" => "x2", "d" => "q1");
$array2[] = array("b" => "y2", "e" => "r1");
function merge_on_key($array1, $array2, $key) {
$result_array = array();
foreach($array1 as $key1 => $sub_array1) {
$merged_array = array();
$sub_array1 = array_filter($sub_array1);
foreach($array2 as $key2 => $sub_array2) {
$sub_array2 = array_filter($sub_array2);
if($sub_array1[$key] == $sub_array2[$key]) {
$merged_array = array_merge($sub_array1, $sub_array2, $merged_array);
unset($array2[$key2]);
}
}
if (!empty($merged_array)) {
$result_array[] = $merged_array;
}
}
return array_merge($result_array, $array2);
}
$final_array = merge_on_key($array1, $array2, "b");
print_r($final_array);
In case you have to match the "b" index within the $array1 itself too, then simply use it twice:
$array1 = merge_on_key($array1, $array1, "b");
$final_array = merge_on_key($array1, $array2, "b");
i really have no idea what you want to achieve here - but based on your description the following code works
$arrA = [
0 =>
[
'a' => 'm1',
'b' => 'x2'
],
1 =>
[
'a' => 'n1',
'b' => 'y2'
]
];
$arrB = [
0 =>
[
'b' => 'x2',
'c' => 'p1',
],
1 =>
[
'b' => 'x2',
'd' => 'q1',
],
2 =>
[
'b' => 'y2',
'e' => 'r1',
],
];
foreach($arrB AS $arrData)
{
foreach($arrData AS $key => $val)
{
if ((isset($arrData['a']) && $arrData['a'] == $arrA[0]['a']) || (isset($arrData['b']) && $arrData['b'] == $arrA[0]['b']))
{
$arrA[0][$key] = $val;
}
elseif ((isset($arrData['b']) && $arrData['b'] == $arrA[1]['a']) || (isset($arrData['b']) && $arrData['b'] == $arrA[1]['b']))
{
$arrA[1][$key] = $val;
}
}
}
print_r($arrA);
Created the arrays similar to yours. $new_array is the resultant array that you are looking for.
$a=array();
$a[0]=array('a'=>'m1', 'b'=>'x2');
$a[1]=array('a'=>'n1', 'b'=>'y2');
$b=array();
$b[0]=array('b'=>'x2', 'c'=>'p1');
$b[1]=array('b'=>'x2', 'd'=>'q1');
$b[2]=array('b'=>'y2', 'e'=>'r1');
foreach($a as $row){
//echo '<pre>'; print_r($row);
foreach($b as $c=>$row1){
//echo '<pre>'; print_r($row1);echo $c;die;
if($row['b']==$row1['b']){
$new_array[]=array_merge($row, $row1);
}
}
}echo '<pre>'; print_r($new_array);
// Gather all values of b from both arrays
$all_b = array_unique(array_merge(array_column($arr1, 'b'), array_column($arr2, 'b')));
$res = [];
// For each b value
foreach($all_b as $b) {
$temp = [];
// Scan the arrays for items with the same b value
foreach($arr1 as $a1) {
if ($a1['b'] == $b) $temp = array_merge($temp, $a1);
}
foreach($arr2 as $a2) {
if ($a2['b'] == $b) $temp = array_merge($temp, $a2);
}
// Save them to new array
$res[] = $temp;
}
print_r($res);
demo on eval

Split array to create an associative array

I have an array that looks like this:
a 12 34
b 12345
c 123456
So the array looks like
$array[0] = "a 12 34"
$array[1] = "b 12345"
$array[2] = "c 123456"
I am trying to create an associative array such that
[a] => 12 34
[b] => 12345
[c] => 123456
Can I possibly split the array into two, one for containing "a, b, c" and another for their contents and use the array_combine()? Or are there any other ways?
You can do that within a loop like the snippet below demonstrates. Quick-Test here:
$array = array("a 12 34", "b 12345", "c 123456");
$array2 = array();
foreach($array as $data){
preg_match("#([a-z])(\s)(.*)#i", $data, $matches);
list(, $key, $space, $value) = $matches;
$array2[$key] = $value;
}
var_dump($array2);
// YIELDS::
array(3) {
["a"]=> string(5) "12 34"
["b"]=> string(5) "12345"
["c"]=> string(6) "123456"
}
Or using a Blend of array_walk() and array_combine() which can be Quick-Tested Here.
<?php
$array = array("a 12 34", "b 12345", "c 123456");
$keys = array();
array_walk($array, function(&$data, $key) use (&$keys){
$keys[] = trim(preg_replace('#(\s.*)#i', '', $data));;
$data = trim(preg_replace('#(^[a-z])#i', '', $data));
});
$array = array_combine($keys, $array);
var_dump($array);;
// YIELDS::
array(3) {
["a"]=> string(5) "12 34"
["b"]=> string(5) "12345"
["c"]=> string(6) "123456"
}
You can do it without any difficulty :) A simple loop is possible to do it.
Create new array
Lopp on each row
Split each data (explode(' ', $row, 2), strstr, substr, ...) ?
Put data on your new array $array[$key] = $value;
You could use a combination of array_map, array_column and array_combine:
$array = array_map(function ($v) { return explode(' ', $v, 2); }, $array);
$array = array_combine(array_column($array, 0), array_column($array, 1));

Operation value of multiple array that have the same key

I Have an array of array
array(4) {
[0]=>
array(3) {
["a"]=>float(1000)
["b"]=>float(3)
["c"]=>float(500)
}
[1]=>
array(3) {
["a"]=>float(1000)
["b"]=>float(852)
["c"]=>float(500)
}
[2]=>
array(3) {
["a"]=>float(1000)
["b"]=>float(5)
["c"]=>float(500)
}
[3]=>
array(1) {
["e"]=>float(1000)
}
}
The result will sum all the value that the same keys,so result should be:
$result =
array(
"a" =>3000,
"b"=>900,
"c"=>1500,
"e"=>1000
)
Anybody could help me todo this.
thanks.
Pseudo:
result <- new array # array holding result
foreach entry1 in array: # iterate outer array
foreach entry2 in entry1: # iterate each inner array
if not exists result[entry2.key]: # if key is not already in result...
result[entry2.key] = 0 # ... add key and set value to zero
result[entry2.key] += value # increment result for key with value from inner array
(I'll leave the implementation as an exercise for OP.)
The trick for this is ofcourse to do some sort of iteration over your data, using those string-keys as identifiers. One way of approaching it would be using 2 nested foreaches ( one over the container, one over the individual keys and collecting the data in a central array:
$results = array();
foreach ($array as $elements)
{
foreach ($elements as $key => $value)
{
if (!isset($results[$key]))
$results[$key] = 0;
$results[$key] += $value;
}
}
A different way would be to have PHP iterate for you:
$results = array();
array_walk_recursive(
$array,
function($value, $key) use (&$results) {
if (!isset($results[$key]))
$results[$key] = 0;
$results[$key] += $value;
}
);
This little function will do the job for you.
function SummarizeFosArray($array) {
$results=array();
foreach ($array as $a) {
foreach ($a as $k=>$v) {
$results[$k]+=$v;
}
}
return $results;
}
Your code
$array = array(
array('a' => 1000, 'b' =>3, 'c'=> 500),
array('a' => 1000, 'b' =>852, 'c'=> 500),
array('a' => 1000, 'b' =>5, 'c'=> 500),
array('e' => 1000)
);
$result = array();
foreach($array as $arr)
{
foreach($arr as $a => $val){
$result[$a] += $val;
}
}
echo "<pre>";
print_r($result);
echo "</pre>";
Your Result
Array
(
[a] => 3000
[b] => 860
[c] => 1500
[e] => 1000
)
use array_walk_recursive() function. checkout the PHP manual for details.

dynamic array key additions

Here is my precode...
$keys = array('a', 'b', 'c', 'd');
$number = 10;
And here is my code...
eval('$array[\''.implode('\'][\'',$keys).'\'] = $number;');
Using this, I get the following result...
Array (
[a] => Array
(
[b] => Array
(
[c] => Array
(
[d] => 10
)
)
)
)
Now, the problem is that this is the exact result I want, but I don't want to use eval().
As input to my code, I have a list of keys and a number. The number should be set to the value of the keys array being used to generate child-based keys for a certain array $array.
Is there a different way that I can achieve this? I don't want to overwrite the keys/numbers with new values as the code works - eval() preserves this already, so my new code should do the same.
Please note that the code below (which you evaluate) will generate a warning, and will therefore not work on projects with error reporting up to the max:
$array = array();
$array['a']['b'] = 42; // $array['a'] is not an array... yet
Since you're using PHP 5, you can work with references to manipulate your array while traversing the branch of your tree that you wish to modify.
$current = & $array;
foreach ($keys as $key):
if (!isset($current[$key]) || !is_array($current[$key]))
$current[$key] = array();
$current = & $current[$key];
endforeach;
$current = $value;
Edit: corrected for avoiding warnings and conflicts.
Here is a full code example showing how it would work. Whats important is that you use a reference to the array so you can modify it:
<?php
$keys = array('a', 'b', 'c', 'd');
$number = 10;
$org_array = array(
"a" => "string",
"z" => array( "k" => false)
);
function write_to_array(&$array, $keys, $number){
$key = array_shift($keys);
if(!is_array($array[$key])) $array[$key] = array();
if(!empty($keys)){
write_to_array($array[$key], $keys, $number);
} else {
$array[$key] = $number;
}
}
write_to_array($org_array, $keys, $number);
print_r($org_array);
?>
function deepmagic($levels, $value)
{
if(count($levels) > 0)
{
return array($levels[0] => deepmagic(array_slice($levels, 1),
$value));
}
else
{
return $value;
}
}
$a = deepmagic(Array('a', 'b', 'c', 'd'), 10);
var_dump($a);
Output:
array(1) {
["a"]=>
array(1) {
["b"]=>
array(1) {
["c"]=>
array(1) {
["d"]=>
int(10)
}
}
}
}

Categories