PHP: Search in 2-Dimensional array but only look in specific key - php

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];
}

Related

Array search on manually generated associative array (different column names)

Lets say I have an array like below
$array = array(
array( //index 0
'col_1' => 'one',
'col_2' => 'two'
),
array( //index 1
'col_5' => 'five',
'col_3' => 'three'
)
);
Now I needed to search for the key value "three" and the output I want to return is 1 (array index). If there is no value exists, I want to return -1.
Actually the flow is,
Search for the value, if exists return it's index. In my scenario the index is 1
So that I can get $array[1]['col_5']
I found a solution here, but in my scenario, the column names will be differ at any time.
For ex.,
$key = array_search('three', array_column($array, '?'));
In the above code, we need to specify the column name, but in my array, the column names are different.
function search($array){
for($i=0;$i<count($array);$i++){
foreach($array[$i] as $key => $value){
if($value=='three'){
return $i;;
}
}
}
return -1;
}
Not sure what exactly you are looking for, but here is some approach:
https://ideone.com/8S6445
$keys = array_map(
function ($el) {
return array_search('three', $el) === false ? -1 : 1;
}, $array);
or probably you wanted something like:
$keys = array_map(
function ($el) {
$idx = array_search('three', $el);
return $idx === false ? -1 : $idx;
}, $array);
I hope finally I've got what is your goal:
$keys = array_map(
function ($el) {
$idx = array_search('three', $el);
if ($idx === false) {
return -1;
} else {
return array_search($idx, array_keys($el));
}
}, $array);
Even if I understood your goal, I don't think that you really need to get index 1 instead of original key col_3 - in most scenarios col_3 is what you would probably use for the following calculations, and by the way same about transformation false to -1. So my guess the function you need should be just:
$keys = array_map(
function ($el) {
return array_search('three', $el);
}, $array);
It will return real key for associative array when element is found and false if not.
Or probably I still did not get your goal?
UPDATE If you just need one value do it straight forward:
https://ideone.com/odTCxI
$key = -1;
foreach($array as $k => $el) {
if (array_search('three', $el) !== false) {
$key = $k;
break;
}
}
echo $key;
You can do this using a customized implementation of the array_filter function. However, just using a `foreach``loop would be just as effective.
Example
$array = [
[
'1' => 'one',
'2' => 'two'
],
[
'1' => 'five',
'2' => 'three',
],
];
// Set our search parameter
$needle = 'three';
$searchResult = current(array_filter(array_keys($array), function ($a) use ($array, $needle) {
return array_search($needle, $array[$a]);
})) ?: -1;
// Output
var_dump($searchResult);

Is it possible to get the key when it is found in array?

Is there a better way to get a key from an array if it is found in case-insensitive search?
The logic of the code I need is like below:
<?php
$search = "foo";
$array = array('Foo' => 1, 'Boo' => 2);
if (array_key_exists($search, array_map('strtolower', $array)))
return "Foo";
?>
Approach I would like to improve:
<?php
if (array_key_exists($search, array_map('strtolower', $array)))
{
foreach($array as $k => $v)
{
if ($search == strtolower($k))
return $k;
}
unset ($k, $v);
}
?>
$a = ['a' => '1', 'b' => '2', 'C' => 3];
$search = 'c';
$result = array_filter($a, function($k) use ($search) {
return strtolower($k) != $search;
}, ARRAY_FILTER_USE_KEY);
var_dump($result);
$search = "foo";
$array = array('Foo' => 1, 'Boo' => 2);
function get_value($array, $search) {
foreach($array as $key => $value) {
if($search === strtolower($key)) {
return $value;
}
}
return null;
}
function get_key($array, $search) {
foreach($array as $key => $value) {
if($search === strtolower($key)) {
return $key;
}
}
return false;
}
echo get_value($array, $search);
var_dump(get_key($array, $search));
I believe you're looking for this approach.
Hope it helps!

Search array by values and return keys

I have values passed c and 3 from $_GET variable, which I want to look up in an array as values and retrieve their keys. How can I search through the array to return accurate keys?
The code below
<?php
$array1 = array(0 => 'a', 1 => 'c', 2 => 'c');
$array2 = array(0 => '3', 1 => '2', 2 => '3');
$key1 = array_search('c', $array1);
$key2 = array_search('3', $array2);
?>
returns
$key1 = 1;
$key2 = 0;
though I am expecting
$key1 = 2;
$key2 = 2;
foreach ($array1 as $key => $value) {
if ($value == 'c' && $array2[$key] == '3') {
echo "The key you are looking for is $key";
break;
}
}
I'm pretty sure there's a saner way to do whatever you're trying to do though.
The function returned exactly as it should have. The first occurrence of value 'c' exists at index 1 in $array1 and the value '3' has its first occurrence at index 0 in $array2
This behavior is documented in the php docs on array_search and it even supplies you with an alternative if you don't like it:
If needle is found in haystack more than once, the first matching key
is returned. To return the keys for all matching values, use
array_keys() with the optional search_value parameter instead.
If you want to find the last key that has that value, you could reverse your array first.
$array1 = array(0 => 'a', 1 => 'c', 2 => 'c');
$array2 = array(0 => '3', 1 => '2', 2 => '3');
$key1 = array_search('c', $array1);
$key2 = array_search('3', $array2);
var_dump($key1,$key2); //output: int(1) int(0)
$key1 = array_search('c', array_reverse($array1, true));
$key2 = array_search('3', array_reverse($array2, true));
var_dump($key1,$key2); //output: int(2) int(2)
Perhaps something like:
<?php
// for specificly 2 arrays
function search_matching($match1, $match2, array $array1, array $array2) {
foreach($array1 as $key1 => $value1) {
// we may want to add $strict = false argument to distinguish between == and === match
// see http://php.net/manual/en/function.array-search.php
if($value1 == $match1 and isset($array2[$key1]) and $array2[$key1] == $match2) {
return $key1;
}
}
return null;
}
// unlimited
function search_matching(array($matches), array $array/*, ...*/) {
if( count($matches) != func_num_args() - 1)
throw new \Exception("Number of values to match must be the same as the number of supplied arrays");
$arrays = func_get_args();
array_shift($arrays); // remove $matches
$array = array_unshift($arrays); // array to be iterated
foreach($array as $key => $value) {
if($value == $matches[0]) {
$matches = true;
foreach($arrays as $keyA => $valueA) {
if(! isset($arrays[$key] or $valueA != $matches[$keyA+1]) {
$matches = false;
break;
}
}
if($matches)
return $key;
}
}
return null;
}
The functions are created with numerical keys in mind.
They could be made cleaner by offloading some functionality to other function, but I wanted to keep it concise and together for the sake of easily seeing how it works

Switch two items in associative array

Example:
$arr = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
I want to switch the positions of grapefruit and pear, so the array will become
$arr = array(
'apple' => 'sweet',
'pear' => 'tasty',
'grapefruit' => 'bitter',
'banana' => 'yellow'
)
I know the keys and values of the elements I want to switch, is there an easy way to do this? Or will it require a loop + creating a new array?
Thanks
Just a little shorter and less complicated than the solution of arcaneerudite:
<?php
if(!function_exists('array_swap_assoc')) {
function array_swap_assoc($key1, $key2, $array) {
$newArray = array ();
foreach ($array as $key => $value) {
if ($key == $key1) {
$newArray[$key2] = $array[$key2];
} elseif ($key == $key2) {
$newArray[$key1] = $array[$key1];
} else {
$newArray[$key] = $value;
}
}
return $newArray;
}
}
$array = $arrOrig = array(
'fruit' => 'pear',
'veg' => 'cucumber',
'tuber' => 'potato',
'meat' => 'ham'
);
$newArray = array_swap_assoc('veg', 'tuber', $array);
var_dump($array, $newArray);
?>
Tested and works fine
Here's my version of the swap function:
function array_swap_assoc(&$array,$k1,$k2) {
if($k1 === $k2) return; // Nothing to do
$keys = array_keys($array);
$p1 = array_search($k1, $keys);
if($p1 === FALSE) return; // Sanity check...keys must exist
$p2 = array_search($k2, $keys);
if($p2 === FALSE) return;
$keys[$p1] = $k2; // Swap the keys
$keys[$p2] = $k1;
$values = array_values($array);
// Swap the values
list($values[$p1],$values[$p2]) = array($values[$p2],$values[$p1]);
$array = array_combine($keys, $values);
}
if the array comes from the db, add a sort_order field so you can always be sure in what order the elements are in the array.
This may or may not be an option depending on your particular use-case, but if you initialize your array with null values with the appropriate keys before populating it with data, you can set the values in any order and the original key-order will be maintained. So instead of swapping elements, you can prevent the need to swap them entirely:
$arr = array('apple' => null,
'pear' => null,
'grapefruit' => null,
'banana' => null);
...
$arr['apple'] = 'sweet';
$arr['grapefruit'] = 'bitter'; // set grapefruit before setting pear
$arr['pear'] = 'tasty';
$arr['banana'] = 'yellow';
print_r($arr);
>>> Array
(
[apple] => sweet
[pear] => tasty
[grapefruit] => bitter
[banana] => yellow
)
Not entirely sure if this was mentioned, but, the reason this is tricky is because it's non-indexed.
Let's take:
$arrOrig = array(
'fruit'=>'pear',
'veg'=>'cucumber',
'tuber'=>'potato'
);
Get the keys:
$arrKeys = array_keys($arrOrig);
print_r($arrKeys);
Array(
[0]=>fruit
[1]=>veg
[2]=>tuber
)
Get the values:
$arrVals = array_values($arrOrig);
print_r($arrVals);
Array(
[0]=>pear
[1]=>cucumber
[2]=>potato
)
Now you've got 2 arrays that are numerical. Swap the indices of the ones you want to swap, then read the other array back in in the order of the modified numerical array. Let's say we want to swap 'fruit' and 'veg':
$arrKeysFlipped = array_flip($arrKeys);
print_r($arrKeysFlipped);
Array (
[fruit]=>0
[veg]=>1
[tuber]=>2
)
$indexFruit = $arrKeysFlipped['fruit'];
$indexVeg = $arrKeysFlipped['veg'];
$arrKeysFlipped['veg'] = $indexFruit;
$arrKeysFlipped['fruit'] = $indexVeg;
print_r($arrKeysFlipped);
Array (
[fruit]=>1
[veg]=>0
[tuber]=>2
)
Now, you can swap back the array:
$arrKeys = array_flip($arrKeysFlipped);
print_r($arrKeys);
Array (
[0]=>veg
[1]=>fruit
[2]=>tuber
)
Now, you can build an array by going through the oringal array in the 'order' of the rearranged keys.
$arrNew = array ();
foreach($arrKeys as $index=>$key) {
$arrNew[$key] = $arrOrig[$key];
}
print_r($arrNew);
Array (
[veg]=>cucumber
[fruit]=>pear
[tuber]=>potato
)
I haven't tested this - but this is what I'd expect. Does this at least provide any kind of help? Good luck :)
You could put this into a function $arrNew = array_swap_assoc($key1,$key2,$arrOld);
<?php
if(!function_exists('array_swap_assoc')) {
function array_swap_assoc($key1='',$key2='',$arrOld=array()) {
$arrNew = array ();
if(is_array($arrOld) && count($arrOld) > 0) {
$arrKeys = array_keys($arrOld);
$arrFlip = array_flip($arrKeys);
$indexA = $arrFlip[$key1];
$indexB = $arrFlip[$key2];
$arrFlip[$key1]=$indexB;
$arrFlip[$key2]=$indexA;
$arrKeys = array_flip($arrFlip);
foreach($arrKeys as $index=>$key) {
$arrNew[$key] = $arrOld[$key];
}
} else {
$arrNew = $arrOld;
}
return $arrNew;
}
}
?>
WARNING: Please test and debug this before just using it - no testing has been done at all.
There is no easy way, just a loop or a new array definition.
Classical associative array doesn't define or guarantee sequence of elements in any way. There is plain array/vector for that. If you use associative array you are assumed to need random access but not sequential. For me you are using assoc array for task it is not made for.
yeah I agree with Lex, if you are using an associative array to hold data, why not using your logic handle how they are accessed instead of depending on how they are arranged in the array.
If you really wanted to make sure they were in a correct order, trying creating fruit objects and then put them in a normal array.
There is no easy way to do this. This sounds like a slight design-logic error on your part which has lead you to try to do this when there is a better way to do whatever it is you are wanting to do. Can you tell us why you want to do this?
You say that I know the keys and values of the elements I want to switch which makes me think that what you really want is a sorting function since you can easily access the proper elements anytime you want as they are.
$value = $array[$key];
If that is the case then I would use sort(), ksort() or one of the many other sorting functions to get the array how you want. You can even use usort() to Sort an array by values using a user-defined comparison function.
Other than that you can use array_replace() if you ever need to swap values or keys.
Here are two solutions. The first is longer, but doesn't create a temporary array, so it saves memory. The second probably runs faster, but uses more memory:
function swap1(array &$a, $key1, $key2)
{
if (!array_key_exists($key1, $a) || !array_key_exists($key2, $a) || $key1 == $key2) return false;
$after = array();
while (list($key, $val) = each($a))
{
if ($key1 == $key)
{
break;
}
else if ($key2 == $key)
{
$tmp = $key1;
$key1 = $key2;
$key2 = $tmp;
break;
}
}
$val1 = $a[$key1];
$val2 = $a[$key2];
while (list($key, $val) = each($a))
{
if ($key == $key2)
$after[$key1] = $val1;
else
$after[$key] = $val;
unset($a[$key]);
}
unset($a[$key1]);
$a[$key2] = $val2;
while (list($key, $val) = each($after))
{
$a[$key] = $val;
unset($after[$key]);
}
return true;
}
function swap2(array &$a, $key1, $key2)
{
if (!array_key_exists($key1, $a) || !array_key_exists($key2, $a) || $key1 == $key2) return false;
$swapped = array();
foreach ($a as $key => $val)
{
if ($key == $key1)
$swapped[$key2] = $a[$key2];
else if ($key == $key2)
$swapped[$key1] = $a[$key1];
else
$swapped[$key] = $val;
}
$a = $swapped;
return true;
}
fwiw here is a function to swap two adjacent items to implement moveUp() or moveDown() in an associative array without foreach()
/**
* #param array $array to modify
* #param string $key key to move
* #param int $direction +1 for down | -1 for up
* #return $array
*/
protected function moveInArray($array, $key, $direction = 1)
{
if (empty($array)) {
return $array;
}
$keys = array_keys($array);
$index = array_search($key, $keys);
if ($index === false) {
return $array; // not found
}
if ($direction < 0) {
$index--;
}
if ($index < 0 || $index >= count($array) - 1) {
return $array; // at the edge: cannot move
}
$a = $keys[$index];
$b = $keys[$index + 1];
$result = array_slice($array, 0, $index, true);
$result[$b] = $array[$b];
$result[$a] = $array[$a];
return array_merge($result, array_slice($array, $index + 2, null, true));
}
There is an easy way:
$sourceArray = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
// set new order
$orderArray = array(
'apple' => '', //this values would be replaced
'pear' => '',
'grapefruit' => '',
//it is not necessary to touch all elemets that will remains the same
);
$result = array_replace($orderArray, $sourceArray);
print_r($result);
and you get:
$result = array(
'apple' => 'sweet',
'pear' => 'tasty',
'grapefruit' => 'bitter',
'banana' => 'yellow'
)
function arr_swap_keys(array &$arr, $key1, $key2, $f_swap_vals=false) {
// if f_swap_vals is false, then
// swap only the keys, keeping the original values in their original place
// ( i.e. do not preserve the key value correspondence )
// i.e. if arr is (originally)
// [ 'dog' => 'alpha', 'cat' => 'beta', 'horse' => 'gamma' ]
// then calling this on arr with, e.g. key1 = 'cat', and key2 = 'horse'
// will result in arr becoming:
// [ 'dog' => 'alpha', 'horse' => 'beta', 'cat' => 'gamma' ]
//
// if f_swap_vals is true, then preserve the key value correspondence
// i.e. in the above example, arr will become:
// [ 'dog' => 'alpha', 'horse' => 'gamma', 'cat' => 'beta' ]
//
//
$arr_vals = array_values($arr); // is a (numerical) index to value mapping
$arr_keys = array_keys($arr); // is a (numerical) index to key mapping
$arr_key2idx = array_flip($arr_keys);
$idx1 = $arr_key2idx[$key1];
$idx2 = $arr_key2idx[$key2];
swap($arr_keys[$idx1], $arr_keys[$idx2]);
if ( $f_swap_vals ) {
swap($arr_vals[$idx1], $arr_vals[$idx2]);
}
$arr = array_combine($arr_keys, $arr_vals);
}
function swap(&$a, &$b) {
$t = $a;
$a = $b;
$b = $t;
}
Well it's just a key sorting problem. We can use uksort for this purpose. It needs a key comparison function and we only need to know that it should return 0 to leave keys position untouched and something other than 0 to move key up or down.
Notice that it will only work if your keys you want to swap are next to each other.
<?php
$arr = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
uksort(
$arr,
function ($k1, $k2) {
if ($k1 == 'grapefruit' && $k2 == 'pear') return 1;
else return 0;
}
);
var_dump($arr);
I'll share my short version too, it works with both numeric and associative arrays.
array array_swap ( array $array , mixed $key1 , mixed $key2 [, bool $preserve_keys = FALSE [, bool $strict = FALSE ]] )
Returns a new array with the two elements swapped. It preserve original keys if specified. Return FALSE if keys are not found.
function array_swap(array $array, $key1, $key2, $preserve_keys = false, $strict = false) {
$keys = array_keys($array);
if(!array_key_exists($key1, $array) || !array_key_exists($key2, $array)) return false;
if(($index1 = array_search($key1, $keys, $strict)) === false) return false;
if(($index2 = array_search($key2, $keys, $strict)) === false) return false;
if(!$preserve_keys) list($keys[$index1], $keys[$index2]) = array($key2, $key1);
list($array[$key1], $array[$key2]) = array($array[$key2], $array[$key1]);
return array_combine($keys, array_values($array));
}
For example:
$arr = array_swap($arr, 'grapefruit', 'pear');
I wrote a function with more general purpose, with this problem in mind.
array with known keys
specify order of keys in a second array ($order array keys indicate key position)
function order_array($array, $order) {
foreach (array_keys($array) as $k => $v) {
$keys[++$k] = $v;
}
for ($i = 1; $i <= count($array); $i++) {
if (isset($order[$i])) {
unset($keys[array_search($order[$i], $keys)]);
}
if ($i === count($array)) {
array_push($keys, $order[$i]);
} else {
array_splice($keys, $i-1, 0, $order[$i]);
}
}
}
foreach ($keys as $key) {
$result[$key] = $array[$key];
}
return $result;
} else {
return false;
}
}
$order = array(1 => 'item3', 2 => 'item5');
$array = array("item1" => 'val1', "item2" => 'val2', "item3" => 'val3', "item4" => 'val4', "item5" => 'val5');
print_r($array); -> Array ( [item1] => val1 [item2] => val2 [item3] => val3 [item4] => val4 [item5] => val5 )
print_r(order_array($array, $order)); -> Array ( [item3] => val3 [item5] => val5 [item1] => val1 [item2] => val2 [item4] => val4 )
I hope this is relevant / helpful for someone
Arrays in php are ordered maps.
$arr = array('apple'=>'sweet','grapefruit'=>'bitter','
pear'=>'tasty','banana'=>'yellow');
doesn't mean that that the first element is 'apple'=>'sweet' and the last - 'banana'=>'yellow' just because you put 'apple' first and 'banana' last. Actually, 'apple'=>'sweet' will be the first and
'banana'=>'yellow' will be the second because of alphabetical ascending sort order.

Transposing multidimensional arrays in PHP

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
)
);

Categories