PHP multidimensional array conversion using array_map/array_walk functions - php

I have the following multidimensional array:
$userList = array(
0 => array('id' => 1000, 'first_name' => 'John', 'last_name' => 'Smith'),
1 => array('id' => 1001, 'first_name' => 'Sam', 'last_name' => 'Johnson'),
);
I want to convert it to the array like:
$userData = array(
1000 => 'John Smith',
1001 => 'Sam Johnson',
);
It's quite obvious for me how to implement this using foreach loop, but I wonder if it's possible to do this with PHP array functions like array_map or array_walk. Please use PHP 5.3 for the callback function. Thank you!

Since those functions only work on the array values, getting them to set the key in the new array is somewhat awkward. One way to do it is:
$arr = array_combine(
array_map(function ($i) { return $i['id']; }, $arr),
array_map(function ($i) { return "$i[first_name] $i[last_name]"; }, $arr)
);
This is a case where a foreach is much more appropriate.

A minor trick from the functional programming.
$arr = array_reduce(
$arr,
function ($result, $item) {
$result[$item['id']] = $item['first_name'] . ' ' . $item['last_name'];
return $result;
},
array()
);
See array_reduce().

Code:
<?php
function convertArray ( $array ) {
$newUserList = array();
foreach ( $array as $value ) {
$newUserList[ $value[ 'id' ] ] = $value[ 'first_name' ] . ' ' . $value[ 'last_name' ];
}
return $newUserList;
}
$userList = array(
0 => array( 'id' => 1000, 'first_name' => 'John', 'last_name' => 'Smith' ),
1 => array( 'id' => 1001, 'first_name' => 'Sam', 'last_name' => 'Johnson' )
);
$newUserList = convertArray( $userList );
?>
Output:
Array
(
[1000] => John Smith
[1001] => Sam Johnson
)

Related

Remove unwanted elements from subarrays in multidimensional array

I have a multidimensional array like this:
[
[
'id' => 1,
'name' => 'John',
'address' => 'Some address 1'
'city' => 'NY'
],
[
'id' => 2,
'name' => 'Jack',
'address' => 'Some address 2'
'city' => 'NY'
]
...
[ ... ]
]
How can I remove elements in all subarrays and only retain the id and name keys with their values?
Would this work?
$result = array_map(function($arr) {
return [
'id' => $arr['id'],
'name' => $arr['name']
];
}, $orig_array);
You want to retain the first two associative elements, so you can make array_slice() calls within array_map(). (Demo)
var_export(
array_map(fn($row) => array_slice($row, 0, 2), $array)
);
Or mapped called of array_intersect_key() against an establish whitelist array. (Demo)
$keep = ['id' => '', 'name' => ''];
var_export(
array_map(
fn($row) => array_intersect_key($row, $keep),
$array
)
)
Or, you could use array destructuring inside of a classic foreach() and make iterated compact() calls. (Demo)
$result = [];
foreach ($array as ['id' => $id, 'name' => $name]) {
$result[] = compact(['id', 'name']);
}
var_export($result);
If you want to edit the same array in place, you can simply iterate over them and unset them.
<?php
$preserve_keys = ['id','name'];
foreach($arr as &$data){
foreach($data as $key => $value){
if(!in_array($key,$preserve_keys)){
unset($data[$key]);
}
}
}
If you want it as a separate result, loop over and add it to the new array.
<?php
$new_result = [];
foreach($arr as $data){
$new_result[] = [
'id' => $data['id'],
'name' => $data['name']
];
}
print_r($new_result);

Convert an array with index and value into an array

I have an array as key => value pair such as:
$array = [ 10 => 'Windows', 12 => 'Keyboard', 15 => 'Monitor' ];
What I would like to achieve without using any foreach or loops the following:
$converted = [
0 => [ 'id' => 10, 'name' => 'Windows'],
1 => [ 'id' => 12, 'name' => 'Keyboard'],
2 => [ 'id' => 15, 'name' => 'Monitor']
];
Here they indices in new array doesn't matter. Any tips??
No foreach and no loop, but now there is a closure:
$result = array_map(function ($id, $name) {
return [
'id' => $id,
'name' => $name
];
}, array_keys($array), array_values($array));
Even if there was a PHP function that did this exactly, it would be using a loop internally.
function do_what_ghazanfar_mir_wants(array $array) {
return array_map(function ($id, $name) {
return [
'id' => $id,
'name' => $name
];
}, array_keys($array), array_values($array));
}
And the single liner is:
$result = do_what_ghazanfar_mir_wants($array);
And the foreach approach for comparison:
$res = [];
foreach ($array as $id => $name) {
$res[] = [ 'id' => $id, 'name' => $name ];
}
If you want to keep it really short then array_walk will do it in one line:
array_walk($array, function(&$value, $key) { $value = ['id' => $key, 'name' => $value]; });
See https://3v4l.org/OEohi
But I think a foreach loop is probably going to be a lot more readable.
Do it with array_map(), Just pass the keys array_keys($array) and values $array as the parameter of your array_map()
<?php
$array = [ 10 => 'Windows', 12 => 'Keyboard', 15 => 'Monitor' ];
function map_by_key($m, $n)
{
return(array('id' => $m, 'name'=>$n));
}
$output = array_map("map_by_key", array_keys($array),$array);
print '<pre>';
print_r($output);
print '</pre>';
?>
DEMO: https://3v4l.org/iTVSm

Counting distinct values in multidimensional array

I'm having real problems trying to figure this one out.
I have a PHP array which looks like this:
$info = array();
$info[0] = array(
'car' => 'Audi',
'previous_car' => 'BMW'
);
$info[1] = array(
'car' => 'Audi',
'previous_car' => 'Seat'
);
$info[2] = array(
'car' => 'Audi',
'previous_carg' => 'BMW'
);
$info[3] = array(
'car' => 'BMW',
'previous_car' => 'BMW'
);
$info[4] = array(
'car' => 'Ford',
'previous_car' => 'Seat'
);
I need to do some sorting on this, so the result looks like this:
Array (
car [
'Audi' => 3,
'BMW' => 1,
'Ford' => 1
],
previous_car [
'BMW' => 3,
'Seat' => 2
]
);
I need to count distinct occurrences of a value in the same key, but the search is made upon couple of arrays. I was trying to use array_value_count(), but I doesn't work well on multidimensional arrays.
I am trying to avoid the looping, since it can be overkill if the array is large.
I will be very grateful for all the help.
If you're running PHP 5.5, you can use:
$newArray = array(
'car' => array_count_values(array_column($info, 'car')),
'previous_car' => array_count_values(array_column($info, 'previous_car'))
);
var_dump($newArray);
For versions of PHP prior to 5.5
$newArray = array(
'car' => array_count_values(
array_map(
function($value) {
return $value['car'];
},
$info
)
),
'previous_car' => array_count_values(
array_map(
function($value) {
return $value['previous_car'];
},
$info
)
)
);
var_dump($newArray);
In a more object orientated way you can solve it as follows
$values = new ArrayObject();
$iterator = new RecursiveArrayIterator($info);
iterator_apply($iterator, 'countDistinct', array($iterator, $values));
function countDistinct($iterator, $values) {
while ( $iterator -> valid() ) {
if ( $iterator -> hasChildren() ) {
countDistinct($iterator -> getChildren(), $values);
} else {
if (!$values->offsetExists($iterator->key())) {
$values->offsetSet($iterator->key(), new ArrayObject());
}
if (!$values->offsetGet($iterator->key())->offsetExists($iterator->current())) {
$values->offsetGet($iterator->key())
->offsetSet($iterator->current(), 1);
} else {
$values->offsetGet($iterator->key())
->offsetSet($iterator->current(),
$values->offsetGet($iterator->key())->offsetGet($iterator->current()) + 1);
}
}
$iterator -> next();
}
}
Sure, with this example you do not avoid the loop. But with the ArrayObject and the RecursiveArrayIterator you will have some memory and performance advantages.
The result of this will exactly match your expected result, which you can easyliy iterate with the getIterator() function of the ArrayObject.
You can write a function that will sort your data but for now check this out:
http://www.php.net/manual/en/function.array-multisort.php
Here is what might help you:
$returnArray = array('car' => NULL, 'previous_car' => NULL);
foreach($info as $newInfo) {
$returnArray['car'][] = $newInfo['car'];
$returnArray['previous_car'][] = $newInfo['previous_car'];
}
$ret['car'] = array_count_values($returnArray['car']);
$ret['previous_car'] = array_count_values($returnArray['previous_car']);
var_dump($ret);
This returns:
array (size=2)
'car' =>
array (size=3)
'Audi' => int 3
'BMW' => int 1
'Ford' => int 1
'previous_car' =>
array (size=2)
'BMW' => int 3
'Seat' => int 2

What PHP Array Function do I use to convert a multidimensional associative array

I want to convert this.
$data1 = array(
array('value' => '100.00', 'total' => '32'),
array('value' => '10.00', 'total' => '13'),
array('value' => '200.00', 'total' => '39'),
array('value' => '190.00', 'total' => '11'),
);
into this
$data2 = array(
'value' => array(0 => '100.00', 1 => '10.00', 2 => '200.00', 3 => '190.00'),
'total' => array(0 => '32', 1 => '13', 2 => '39', 3 => '11')
);
I can obviously do this in a roundabout way by iterating over the top array, while appending to a series of arrays, but I figured that there must be a php array function that I don't know about that can do this more concisely.
http://www.php.net/manual/en/ref.array.php
Values are floats and integers (if it makes any difference), I've just added them as strings in the example code because it's easier to read IMO. Final array order should match the initial order. I'll award the correct answer to the least LOC providing performance isn't significantly worse that the 'long' version. PHP 5.4.
If PHP had an array_pluck function, it would be simple.
function array_pluck(array $array, $field)
{
return array_map(function($row) use ($field) { return $row[$field]; }, $array);
}
$data2 = array(
'value' => array_pluck($data1, 'value'),
'total' => array_pluck($data1, 'total')
);
I think that's about as easy as it is to read, but you'll be looping over the entire array once per field, so it's hardly the optimal solution.
Personally, this is a situation where I'd probably stick with the foreach solution but try to wrap it inside some reusable function.
<?php // php 5.4 array syntax
$new = array_reduce($data1, function (&$result, $item)
{
$result['value'][] = $item['value'];
$result['total'][] = $item['total'];
return $result;
},
['value' => [], 'total' => []]);
'value' and 'total' are arbitrary names, so you're not going to get a one-liner php library function to do this.
You can refactor this code into a function if you want to...
function array_rotate($data) {
$k = array_keys($data[0]);
return array_reduce($data, function (&$r, $i) use ($k) {
$r[$k[0]][] = $i[$k[0]];
$r[$k[1]][] = $i[$k[1]];
return $r;
}, [$k[0] => [], $k[1] => []]);
}
I generalised #matthew's code, this allows an arbitrary number of keys (instead of 2):
function array_rotate2($data) {
return array_combine(array_keys($data[0]),
array_map(function ($field) use ($data) {
return array_map(function($row) use ($field) { return $row[$field]; }, $data);
}, array_keys($data[0])));
}
You can do it without a function using a simple foreach():
<?php
$data = array(
array('value' => '100.00', 'total' => '32'),
array('value' => '10.00', 'total' => '13'),
array('value' => '200.00', 'total' => '39'),
array('value' => '190.00', 'total' => '11'),
);
$newArray = array();
$i=0;
foreach($data as $value){
$newArray["value"][] = $data[$i]["value"];
$newArray["total"][] = $data[$i]["total"];
$i++;
}
echo "<pre>";
print_r($newArray);
echo "</pre>";
?>
Prints this:
Array
(
[value] => Array
(
[0] => 100.00
[1] => 10.00
[2] => 200.00
[3] => 190.00
)
[total] => Array
(
[0] => 32
[1] => 13
[2] => 39
[3] => 11
)
)
$final = array();
foreach($data1 as $array) {
foreach($array as $key => $value) {
$final[$key] = isset($final[$key]) ? $final : array();
$final[$key][] = $value;
}
}
Like others are saying I don't think there is a one-liner. Here is a reusable foreach function that should work
function array_multi_key_combine($a, $keys = array()) {
$b = array();
foreach($a as $v) {
foreach($keys as $k) {
if(isset($v[$k])) $b[$k][] = $v[$k];
}
}
return $b;
}
$data2 = array_multi_key_combine($data1, array('value', 'total'));

Isolate a single column in a multi-dimensional array

Say for example you just queried a database and you recieved this 2D array.
$results = array(
array('id' => 1, 'name' => 'red' , 'spin' => 1),
array('id' => 2, 'name' => 'green', 'spin' => -1),
array('id' => 3, 'name' => 'blue' , 'spin' => .5)
);
I often find myself writing loops like this.
foreach($results as $result)
$names[] = $result['name'];
My questions is does there exist a way to get this array $names without using a loop? Using callback functions count as using a loop.
Here is a more generic example of getting every field.
foreach($results as $result)
foreach($result as $key => $value)
$fields[$key][] = $value;
As of June 20th in PHP-5.5 there is a new function array_column
For example:
$records = array(
array(
'id' => 2135,
'first_name' => 'John',
'last_name' => 'Doe'
),
array(
'id' => 3245,
'first_name' => 'Sally',
'last_name' => 'Smith'
),
array(
'id' => 5342,
'first_name' => 'Jane',
'last_name' => 'Jones'
),
array(
'id' => 5623,
'first_name' => 'Peter',
'last_name' => 'Doe'
)
);
$firstNames = array_column($records, 'first_name');
print_r($firstNames);
Will return
Array
(
[0] => John
[1] => Sally
[2] => Jane
[3] => Peter
)
There are even more examples in the above mentioned link.
I voted #Devon's response up because there really isn't a way to do what you're asking with a built-in function. The best you can do is write your own:
function array_column($array, $column)
{
$ret = array();
foreach ($array as $row) $ret[] = $row[$column];
return $ret;
}
Starting PHP 5.3, you can use this pretty call with lambda function:
$names = array_map(function ($v){ return $v['name']; }, $results);
This will return array sliced by 'name' dimension.
Simply put, no.
You will need to use a loop or a callback function like array_walk.
I did more research on this and found that ruby and prototype both have a function that does this called array_pluck,2. It's interesting that array_map has a second use that allows you to do the inverse of what i want to do here. I also found a PHP class someone is writing to emulate prototypes manipulation of arrays.
I'm going to do some more digging around and if I don't find anything else I'll work on a patch to submit to the internals#lists.php.net mailing list and see if they will add array_pluck.
For those of you that cannot upgrade to PHP5.5 right now and need this function, here is an implementation of array_column.
function array_column($array, $column){
$a2 = array();
array_map(function ($a1) use ($column, &$a2){
array_push($a2, $a1[$column]);
}, $array);
return $a2;
}
If you are running a version of PHP before 5.5 and array_column(), you can use the official replacement in plain PHP:
https://github.com/ramsey/array_column
I think this will do what you want
array_uintersect_uassoc
You would have to do something like this
$results = array(
array('id' => 1, 'name' => 'red' , 'spin' => 1),
array('id' => 2, 'name' => 'green', 'spin' => -1),
array('id' => 3, 'name' => 'blue' , 'spin' => .5)
);
$name = array_uintersect_uassoc( $results, array('name' => 'value') , 0, "cmpKey");
print_r($name);
//////////////////////////////////////////////////
// FUNCTIONS
//////////////////////////////////////////////////
function cmpKey($key1, $key2) {
if ($key1 == $key2) {
return 0;
} else {
return -1;
}
}
However, I don't have access to PHP5 so I haven't tested this.
You could do:
$tmp = array_flip($names);
$names = array_keys($tmp);
This is fast function alternative of array_column()
if(!function_exists('array_column')) {
function array_column($element_name) {
$ele = array_map(function($element) {
return $element[$element_name];
}, $a);
return $ele;
}
}
other alternative
function transpose(array $array): array
{
$out = array();
foreach ($array as $rowkey => $row) {
foreach ($row as $colkey => $col) {
$out[$colkey][$rowkey] = $col;
}
}
return $out;
}
function filter_columns(array $arr, string ...$columns): array
{
return array_intersect_key($arr, array_flip($columns));
}
test
$results = array(
array('id' => 1, 'name' => 'red' , 'spin' => 1),
array('id' => 2, 'name' => 'green', 'spin' => -1),
array('id' => 3, 'name' => 'blue' , 'spin' => .5)
);
var_dump(filter_columns(transpose($results),'name'));
var_dump(filter_columns(transpose($results),'id','name'));
var_dump(filter_columns(transpose($results),'id','spin'));

Categories