Related
Is there built-in function, or shorter way to extract elements into new array, as described here?
<?php
function arr_slice ($arr, $keys) {
$ret = array();
foreach ($keys as $k) { $ret[$k] = $arr[$k]; }
return $ret;
}
$array = array(
"a" => 1,
"b" => 2,
"c" => 3,
"d" => 4,
);
var_export(
arr_slice($array, array("x","d","b"))
);
output (key order matters)
array (
'x' => NULL,
'd' => 4,
'b' => 2,
)
Suppose you have this array:
$object = array ('a' => array ( 'b' => array('c' = 'value'), 'd' => 3));
$indices = array ('a', 'b', 'c');
Is there an easy way to access $object['a']['b']['c'] (keys from $indices array)?
This is what I tried:
function accessObjectKey ($object, $levels) {
if (is_string($levels))
$levels = explode ('.', $levels);
//
for ($i=0; $i<count($levels); $i++) {
if ($i == count($levels) && key_exists($levels[$i], $object)) {
$value = $object[$levels[$i]];
}
else {
$value = accessObjectKey ($object, array_shift($levels));
}
}
return $value;
}
Thank you
First of all (you have typo array('c' = 'value'), should be array('c' => 'value'),)
I tried to use your code:
$object = array ('a' => array ( 'b' => array('c' => 'value'), 'd' => 3));
$indices = array ('a', 'b', 'c');
echo accessObjectKey($object,$indices);
that returned me an error:
Fatal error: Maximum function nesting level of '100' reached,
aborting!
I was little bit confusing by your function code, so sorry I created mine
It is not perfect but return value as expected:
function getByPath($arr,$path) {
if (!isset($arr[$path[0]])) {
return 'There is no '.$path[0].' element!';
} elseif(count($path)==1) {
return $arr[$path[0]];
} elseif(!is_array($arr[$path[0]])) {
return 'Element '.$path[0].' is not array! ';
} else {
$key = array_shift($path);
return getByPath($arr[$key],$path);
}
}
$object = array ('a' => array ( 'b' => array('c' => 'value'), 'd' => 3));
$indices = array ('a', 'b', 'c');
echo getByPath($object,$indices);
output:
value
Here's another option, from https://baohx2000.medium.com/reaching-deep-into-arrays-using-array-reduce-in-php-9ff9e39a9ca8
function getArrayPath(array $path, array $deepArray) {
$reduce = function(array $xs, $x) {
return (
array_key_exists($x, $xs)
) ? $xs[$x] : null;
};
return array_reduce($path, $reduce, $deepArray);
}
$x = [
'a' => 1,
'b' => [
'c' => 2,
'd' => [3, 4, 5],
],
];
print_r(getArrayPath(['b','d', 0], $x)); // 3
Another technique is to use explicit stacking.
I came upon this page while searching for related techniques for this CodeReview question.
By "stacking", I actually mean iterate the array of keys which dictate the path to the desired array value and each time a key is found, overwrite the parent array with the newly encountered subarray.
Code: (Demo)
function getFromKeyPath(array $array, array $keys) {
foreach ($keys as $index => $key) {
if (!key_exists($key, $array)) {
throw new Exception("key $key not found at level index $index");
}
$array = $array[$key];
}
return $array;
}
$array = ['a' => ['b' => ['c' => 'value'], 'd' => 3]];
$keys = ['a', 'b', 'c'];
try {
var_export(getFromKeyPath($array, $keys));
} catch (Exception $e) {
echo $e->getMessage();
}
// 'value'
This question already has answers here:
How to sum all column values in multi-dimensional array?
(20 answers)
Closed 9 years ago.
i need to merge two associative arrays which may or may not contain same key,if key is same the values need to added and stored in the resultant array
$array1 = array(
'a' => 5,
'b' => 10,
'c' => 6,
'k' = > 10
);
$array2 = array(
'a' => 100,
'c' => 200,
'd' => 30,
'k' => 10
);
how to add the above two associative arrays and receive the following output/associative array -
$array2 = array(
'a' => 105,
'b' => 10,
'c' => 206,
'd' => 30,
'k' => 20
);
Try
$array1 = array(
'a' => 5,
'b' => 10,
'c' => 6,
'k' => 10
);
$array2 = array(
'a' => 100,
'c' => 200,
'd' => 30,
'k' => 10
);
$sums = array();
foreach (array_keys($array1 + $array2) as $key) {
$sums[$key] = (isset($array1[$key]) ? $array1[$key] : 0) + (isset($array2[$key]) ? $array2[$key] : 0);
}
print_r($sums);
$a3 = array_merge_recursive ($array1, $array2);
foreach ($a3 as $key => $value)
{
$a3[$key] = (is_array($value))?array_sum($value):$value;
}
for finding unique index value
$array2 =array_merge($array1,$array2);
and for finding unique value you can try
$array2 =array_unique(array_merge($array1,$array2));
I don't know if you can do this with an internal function but it is doable by simply iterating through both arrays.
function merge_add($array1, $array2)
{
$result = array();
merge_add_array($array1, $result);
merge_add_array($array2, $result);
return $result;
}
function merge_add_array($array, &$result)
{
foreach ($array as $key=>$value)
{
if (array_key_exists($key, $result))
{
$result[$key] += $value;
}
else
{
$result[$key] = $value;
}
}
}
$result = merge_add($array1, $array2);
The & will cause passing by reference
<?php
$array1 = array(
'a' => 5,
'b' => 10,
'c' => 6,
'k' => 10
);
$array2 = array(
'a' => 100,
'c' => 200,
'd' => 30,
'k' => 10
);
$keys = array_keys($array1 + $array2);
foreach ($keys as $key) {
$array2[$key] = isset($array1[$key]) && isset($array2[$key]) ? ($array1[$key] + $array2[$key]) : (isset($array1[$key]) ? $array1[$key] : $array2[$key]);
}
print_r($array2);
How can I search for a key in an array that contains more arrays.
For example I would like to search for "key" in "arr" and return this:
arr["some_inner_array"]["another_array_possible"][key"]
array_key_exists can tell me if it exists, but of course what I really need in the value...
I hope my question is clear...
EDIT:
based on the answer below, I managed to do a recursive function for that:
function look_in_array ( $array, $key ) {
if ( isset($array[$key]) )
return $array[$key];
foreach ($array as $item) {
if (is_array($item)) {
$value = look_in_array ($item,$key);
if ($value)
return $value;
}
}
}
This function should work:
function array_key_exists_recursive($searchKey, $array)
{
$result = false;
foreach($array as $key => $value)
{
if(is_array($array[$key]))
{
$result = array_key_exists_recursive($searchKey, $array[$key]);
}
else if(array_key_exists($searchKey, $array))
{
$result = $array[$searchKey];
}
if($result)
break;
}
return $result;
}
Exmaple:
$array = array( "a" => array("b" => "1", "c" => "2") );
var_dump(array_key_exists_recursive("c", $array)); //Result: 2
You could just ask this:
isset(arr["some_inner_array"]["another_array_possible"]["key"])
Would this work for you? Otherwise maybe explaning a bit better about what you're trying to accomplish would help us help you :)
You need to iterate over all elements in the first arrays and then use array_key_exists():
foreach($arr as $inner1) {
foreach($inner1 as $inner2) {
if array_key_exists($inner2, $key) {
echo $inner2[$key];
break 2; // if you only want the first match
}
}
}
Try this:
/**
* #param $path array containing path
* #param $array search array
* #return element matching path or null
*/
function arr_search($path, &$array){
$tmp = &$array;
for($i = 0; $i < count($path); $i++){
if(!isset($tmp[$path[$i]])) return null;
$tmp = &$tmp[$path[$i]];
}
return $tmp;
}
$arr = array(
'a' => array(
'b' => array(
'c' => 'abc',
),
'd' => array('ad'),
),
10 => array(100, 200, 300),
);
var_dump(arr_search(array('a', 'b', 'c'), $arr));
var_dump(arr_search(array('a', 'd'), $arr));
var_dump(arr_search(array(10, 100), $arr));
var_dump(arr_search(array(10, 1), $arr));
// EDIT previous example was wrong, so here is new one :)
function arr_search($key, $array){
$values = array();
if(array_key_exists($key, $array)) $values[] = $array[$key];
$stack = array_values($array);
while($tmp = array_pop($stack)){
if(is_array($tmp)){
foreach($tmp as &$v){
array_push($stack, $v);
}
if(array_key_exists($key, $tmp)){
$values[] = $tmp[$key];
}
}
}
return $values;
}
$arr = array(
'a' => array(
'b' => array(
'c' => 'abc',
),
'd' => array('ad'),
),
'e' => array(
'a' => array(
'b' => 'abc',
),
'b' => array('xyz'),
),
10 => array(100, 200, 300),
);
var_dump(arr_search('b', $arr));
var_dump(arr_search(0, $arr));
How would you flip 90 degrees (transpose) a multidimensional array in PHP? For example:
// Start with this array
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
$bar = flipDiagonally($foo); // Mystery function
var_dump($bar[2]);
// Desired output:
array(3) {
["a"]=>
string(2) "a2"
["b"]=>
string(2) "b2"
["c"]=>
string(2) "c2"
}
How would you implement flipDiagonally()?
Edit: this is not homework. I just want to see if any SOers have a more creative solution than the most obvious route. But since a few people have complained about this problem being too easy, what about a more general solution that works with an nth dimension array?
i.e. How would you write a function so that:
$foo[j][k][...][x][y][z] = $bar[z][k][...][x][y][j]
?(ps. I don't think 12 nested for loops is the best solution in this case.)
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
Or if you're using PHP 5.6 or later:
function transpose($array) {
return array_map(null, ...$array);
}
With 2 loops.
function flipDiagonally($arr) {
$out = array();
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
}
I think you're referring to the array transpose (columns become rows, rows become columns).
Here is a function that does it for you (source):
function array_transpose($array, $selectKey = false) {
if (!is_array($array)) return false;
$return = array();
foreach($array as $key => $value) {
if (!is_array($value)) return $array;
if ($selectKey) {
if (isset($value[$selectKey])) $return[] = $value[$selectKey];
} else {
foreach ($value as $key2 => $value2) {
$return[$key2][$key] = $value2;
}
}
}
return $return;
}
Transposing an N-dimensional array:
function transpose($array, &$out, $indices = array())
{
if (is_array($array))
{
foreach ($array as $key => $val)
{
//push onto the stack of indices
$temp = $indices;
$temp[] = $key;
transpose($val, $out, $temp);
}
}
else
{
//go through the stack in reverse - make the new array
$ref = &$out;
foreach (array_reverse($indices) as $idx)
$ref = &$ref[$idx];
$ref = $array;
}
}
$foo[1][2][3][3][3] = 'a';
$foo[4][5][6][5][5] = 'b';
$out = array();
transpose($foo, $out);
echo $out[3][3][3][2][1] . ' ' . $out[5][5][6][5][4];
Really hackish, and probably not the best solution, but hey it works.
Basically it traverses the array recursively, accumulating the current indicies in an array.
Once it gets to the referenced value, it takes the "stack" of indices and reverses it, putting it into the $out array. (Is there a way of avoiding use of the $temp array?)
Codler's answer fails for a single-row matrix (e.g. [[1,2]]) and also for the empty matrix ([]), which must be special-cased:
function transpose(array $matrix): array {
if (!$matrix) return [];
return array_map(count($matrix) == 1 ? fn ($x) => [$x] : null, ...$matrix);
}
(note: PHP 7.4+ syntax, easy enough to adapt for older versions)
I got confronted with the same problem. Here is what i came up with:
function array_transpose(array $arr)
{
$keys = array_keys($arr);
$sum = array_values(array_map('count', $arr));
$transposed = array();
for ($i = 0; $i < max($sum); $i ++)
{
$item = array();
foreach ($keys as $key)
{
$item[$key] = array_key_exists($i, $arr[$key]) ? $arr[$key][$i] : NULL;
}
$transposed[] = $item;
}
return $transposed;
}
I needed a transpose function with support for associative array:
$matrix = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
$result = \array_transpose($matrix);
$trans = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
And the way back:
$matrix = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
$result = \array_transpose($matrix);
$trans = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
The array_unshift trick did not work NOR the array_map...
So I've coded a array_map_join_array function to deal with record keys association:
/**
* Similar to array_map() but tries to join values on intern keys.
* #param callable $callback takes 2 args, the intern key and the list of associated values keyed by array (extern) keys.
* #param array $arrays the list of arrays to map keyed by extern keys NB like call_user_func_array()
* #return array
*/
function array_map_join_array(callable $callback, array $arrays)
{
$keys = [];
// try to list all intern keys
array_walk($arrays, function ($array) use (&$keys) {
$keys = array_merge($keys, array_keys($array));
});
$keys = array_unique($keys);
$res = [];
// for each intern key
foreach ($keys as $key) {
$items = [];
// walk through each array
array_walk($arrays, function ($array, $arrKey) use ($key, &$items) {
if (isset($array[$key])) {
// stack/transpose existing value for intern key with the array (extern) key
$items[$arrKey] = $array[$key];
} else {
// or stack a null value with the array (extern) key
$items[$arrKey] = null;
}
});
// call the callback with intern key and all the associated values keyed with array (extern) keys
$res[$key] = call_user_func($callback, $key, $items);
}
return $res;
}
and array_transpose became obvious:
function array_transpose(array $matrix)
{
return \array_map_join_array(function ($key, $items) {
return $items;
}, $matrix);
}
We can do this by using Two foreach. Traveling one array and another array to create new arrayLike This:
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
$newFoo = [];
foreach($foo as $a => $k){
foreach($k as $i => $j){
$newFoo[$i][]= $j;
}
}
Check The Output
echo "<pre>";
print_r($newFoo);
echo "</pre>";
Before I start, I'd like to say thanks again to #quazardus for posting his generalised solution for tranposing any two dimenional associative (or non-associative) array!
As I am in the habit of writing my code as tersely as possible I went on to "minimizing" his code a little further. This will very likely not be to everybody's taste. But just in case anyone should be interested, here is my take on his solution:
function arrayMap($cb, array $arrays) // $cb: optional callback function
{ $keys = [];
array_walk($arrays, function ($array) use (&$keys)
{ $keys = array_merge($keys, array_keys($array)); });
$keys = array_unique($keys); $res = [];
foreach ($keys as $key) {
$items = array_map(function ($arr) use ($key)
{return isset($arr[$key]) ? $arr[$key] : null; },$arrays);
$res[$key] = call_user_func(
is_callable($cb) ? $cb
: function($k, $itms){return $itms;},
$key, $items);
}
return $res;
}
Now, analogous to the PHP standard function array_map(), when you call
arrayMap(null,$b);
you will get the desired transposed matrix.
This is another way to do the exact same thing which #codler s answer does. I had to dump some arrays in csv so I used the following function:
function transposeCsvData($data)
{
$ct=0;
foreach($data as $key => $val)
{
//echo count($val);
if($ct< count($val))
$ct=count($val);
}
//echo $ct;
$blank=array_fill(0,$ct,array_fill(0,count($data),null));
//print_r($blank);
$retData = array();
foreach ($data as $row => $columns)
{
foreach ($columns as $row2 => $column2)
{
$retData[$row2][$row] = $column2;
}
}
$final=array();
foreach($retData as $k=>$aval)
{
$final[]=array_replace($blank[$k], $aval);
}
return $final;
}
Test and output reference: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/
Here is array_walk way to achieve this,
function flipDiagonally($foo){
$temp = [];
array_walk($foo, function($item,$key) use(&$temp){
foreach($item as $k => $v){
$temp[$k][$key] = $v;
}
});
return $temp;
}
$bar = flipDiagonally($foo); // Mystery function
Demo.
Here's a variation of Codler/Andreas's solution that works with associative arrays. Somewhat longer but loop-less purely functional:
<?php
function transpose($array) {
$keys = array_keys($array);
return array_map(function($array) use ($keys) {
return array_combine($keys, $array);
}, array_map(null, ...array_values($array)));
}
Example:
<?php
$foo = array(
"fooA" => [ "a1", "a2", "a3"],
"fooB" => [ "b1", "b2", "b3"],
"fooC" => [ "c1", "c2", "c3"]
);
print_r( transpose( $foo ));
// Output like this:
Array (
[0] => Array (
[fooA] => a1
[fooB] => b1
[fooC] => c1
)
[1] => Array (
[fooA] => a2
[fooB] => b2
[fooC] => c2
)
[2] => Array (
[fooA] => a3
[fooB] => b3
[fooC] => c3
)
);