Related
I want to search through a 2-dimensional array, but I only want to search in a specific field in the 2nd Dimension. If found, I would like to return the Key. No need to go on from there, I only need the first occurence but I wouldn't mind to get all occurences either.
The Array might look like this:
$array = [
0 => ['value' => 'x', 'foo' => 'bar'],
1 => ['value' => 'y', 'foo' => 'bar'],
2 => ['value' => 'z', 'foo' => 'x'],
];
Now my first thought would be something like this:
function myCustomArraySearch($array, $searchkey, $searchvalue) {
foreach ($array as $key => $value) {
if ($value[$searchkey] == $searchvalue) {
return $key;
}
}
return false;
}
echo myCustomArraySearch($array, 'value', 'x');
I'm sure, there is a more elegnt solution. Any ideas?
Here is one way that returns a single key:
$result = array_search('x', array_column($array, 'value'));
This will return multiple keys:
$result = array_keys(array_column($array, 'value'), 'x');
If you don't have PHP >= 5.5.0 needed for array_column() then use this in it's place:
array_map(function($v) { return $v['value']; }, $array)
The functions below returns the position of the first occurrence:
1 - Using foreach iteration and array_serach
function search1($array, $key, $value) {
foreach ($array as $k => $arr) {
if (array_search($value, $arr) != false) {
return $k;
}
}
return false;
}
2 - Using array_map, array_key_exists and array_search.
function search2($array, $key, $value) {
$mapped = array_map(function($arr) use ($key, $value) {
return (array_key_exists($key, $arr) && $arr[$key] == $value)
? true
: false;
},
$array);
return array_search(true, $mapped);
}
Your code is working fine so this code just does it in less lines. Only works for PHP 5.5+.
function myCustomArraySearch($array, $searchkey, $searchvalue) {
$cols = array_column($array, $searchkey);
$result = array_search($searchvalue, $cols);
return $result;
}
Of course, if you wanted to return the array it found and not just the index you would just return like so:
function myCustomArraySearch($array, $searchkey, $searchvalue) {
$cols = array_column($array, $searchkey);
$result = array_search($searchvalue, $cols);
return $array[$result];
}
Array1
(
[a]=>1; [b]=>2; [c]=>3
)
Array2
(
[a]=>1;[b] =>1
)
Required result:
Array1
(
[a]=>2; [b]=>3; [c]=>3
)
How do i append Array1 with the values of Array2 based on their key? Thanks.
You can try something like this:
foreach($array2 as $key2 => $val2){
if(key_exists($key2, $array1)) {
$array1[$key2] += $val2;
} else {
$array1[$key2] = $val2;
}
}
Part of the issue would be that array 1 may not have all of the same keys as array 2. So, an array of all keys from both original arrays is needed, then loop through those keys, check if it exists in either original array, and finally add it to the final combined array.
<?php
$array1 = array('a' => 1, 'b' => 2, 'c' => 3);
$array2 = array('a' => 1, 'b' => 2, 'd' => 3);
$finalarr = array();
$arrkeys = array_merge(array_keys($array1), array_keys($array2));
$arrkeys = array_unique($arrkeys);
foreach($arrkeys as $key) {
$finalarr[$key] = 0;
if (isset($array1[$key])) {
$finalarr[$key] += $array1[$key];
}
if (isset($array2[$key])) {
$finalarr[$key] += $array2[$key];
}
}
print_r($finalarr);
?>
foreach($array1 as $key=>$value){
if(isset($array2[$key])){
$array1[$key] = $array1[$key] + $array2[$key];
}
}
Not the most elegant way, which would use array_walk or array_map, but I like to see and know exactly what's going on. This will give you what you are looking for.
First sum the values of the common keys and after that, add the others key in the other array:
foreach ($array2 as $k2 => $a2){
if (isset($array1[$k2])){
$array1[$k2]+=$a2;
unset($array2[$k2]);
}
}
$array1 += $array2;
Something like:
$result = array();
function ParseArray(array $array, array &$result)
{
foreach ($array as $k => $v) {
if (!array_key_exists($k, $result) {
$result[$k] = $v;
} else {
$result[$k] += $v;
}
}
}
ParseArray($Array1, $result);
ParseArray($Array2, $result);
print_r($result);
You should read about PHP array functions.
$array1 = array(
'a' => 1,
'b' => 2,
'c' => 3,
);
$array2 = array(
'a' => 1,
'b' => 1,
);
array_walk(
$array1,
function (&$value, $key) use ($array2) {
$value += (isset($array2[$key])) ? $array2[$key] : 0;
}
);
var_dump($array1);
Assume I have the following function
function setArray(&$array, $key, $value)
{
$array[$key] = $value;
}
In the above function, key is only at the first level, what if I want to set the key at the 2nd or 3rd levels, how to rewrite the function?
e.g.
$array['foo']['bar'] = 'test';
I want to use the same function to set the array value
This one should work. Using this function you can set any array element in any depth by passing a single string containing the keys separated by .
function setArray(&$array, $keys, $value) {
$keys = explode(".", $keys);
$current = &$array;
foreach($keys as $key) {
$current = &$current[$key];
}
$current = $value;
}
You can use this as follows:
$array = Array();
setArray($array, "key", Array('value' => 2));
setArray($array, "key.test.value", 3);
print_r($array);
output:
Array (
[key] => Array
(
[value] => 2
[test] => Array
(
[value] => 3
)
)
)
You can use array_merge_recursive
$array = array("A" => "B");
$new['foo']['bar'] = 'test';
setArray($array, $new);
var_dump($array);
Output
array (size=2)
'A' => string 'B' (length=1)
'foo' =>
array (size=1)
'bar' => string 'test' (length=4)
Function Used
function setArray(&$array, $value) {
$array = array_merge_recursive($array, $value);
}
this function should do it, the key should be an array, for example array('foo', 'bar')
function setArray(&$array, array $keys, $value) {
foreach($keys as $key) {
if(!isset($array[$key])) {
$array[$key] = array();
}
$array = &$array[$key];
}
$array = $value;
}
$arr = array();
setArray($arr, array('first', 'second'), 1);
var_dump($arr);
// dumps array(1) { ["first"]=> array(1) { ["second"]=> int(1) } }
Tested and works.
Use array_replace_recursive instead of array_merge_recursive as it handles same-key scenario correctly. See this https://3v4l.org/2ICmo
Just like this:
function setArray(&$array, $key1, $key2, $value)
{
$array[$key1][$key2] = $value;
}
But why you want to use function? Using it like this:
setArray($array, 'foo', 'bar', 'test');
takes more time to write something like this:
$array[1][2] = 'test';
I need to merge associative arrays and group by the name. Say I have such 3 arrays:
ARRAY1
"/path/file.jpg" => 2,
"/path/file2.bmp" => 1,
"/file3.gif" => 5,
ARRAY2
"/path/file.jpg" => 1,
"/path/file2.bmp" => 1,
"/file3.gif" => 0,
ARRAY3
"/path/file.jpg" => 1,
"/path/file2.bmp" => 1,
I need to merge these arrays to one and group them by filepath and have result of sum of their values. Something like:
SELECT filename, SUM(val) FROM files
GROUP BY filename
But with multiple input arrays. Arrays are short (around 20 elements max). Each array might have different size.
[EDIT: I adapted the function (as suggested by John Green) to use func_get_args so you don't need to put all the seperate arrays in one array before you can use it.]
I think you could use the following function.
mergeArrays()
{
$return = array();
$arrays = func_get_args();
foreach ($arrays as $array) {
foreach ($array as $key => $val) {
if (array_key_exists($key, $array) {
$return[$key] += $val;
} else {
$return[$key] = $val;
}
}
}
return $return;
}
one possible way
$rtn = array();
foreach ($array1 as $key=>$val)
{
$rtn[$key]+=$val;
}
foreach ($array2 as $key=>$val)
{
$rtn[$key]+=$val;
}
foreach ($array2 as $key=>$val)
{
$rtn[$key]+=$val;
}
the above will assign the filename, SUM(val) as an associative array into $rtn
You can use a RecursiveArrayIterator
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($paths));
foreach ($iterator as $path => $value) {
$summed[$path] = isset($summed[$path]) ? $summed[$path] + $value : $value;
}
print_r($summed);
or array_walk_recursive and a Closure
$summed = array();
array_walk_recursive($paths, function($value, $path) use (&$summed) {
$summed[$path] = isset($summed[$path]) ? $summed[$path] + $value : $value;
});
Both will give
Array
(
[/path/file.jpg] => 4
[/path/file2.bmp] => 3
[/file3.gif] => 5
)
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
)
);