Get a subset of an array based on an array of keys - php

I wrote this function to get a subset of an array. Does php have a built in function for this. I can't find one in the docs. Seems like a waste if I'm reinventing the wheel.
function array_subset($array, $keys) {
$result = array();
foreach($keys as $key){
$result[$key] = $array[$key];
}
return $result;
}

I always want this too. Like a PHP version of Underscore's pick.
It's ugly and counter-intuitive, but what I sometimes do is this (I think this may be what prodigitalson was getting at):
$a = ['foo'=>'bar', 'zam'=>'baz', 'zoo'=>'doo'];
// Extract foo and zoo but not zam
print_r(array_intersect_key($a, array_flip(['foo', 'zoo'])));
/*
Array
(
[foo] => bar
[zoo] => doo
)
*/
array_intersect_key returns all the elements of the first argument whose keys are present in the 2nd argument (and all subsequent arguments, if any). But, since it compares keys to keys, I use array_flip for convenience. I could also have just used ['foo' => null, 'zoo' => null] but that's even uglier.

array_diff_key and array_intersect_key are probably what you want.

There is no direct function I think in PHP to get a subset from an array1 with compare to another array2 where the values are the list of key name which we fetch.
Like: array_only($array1, 'field1','field2');
But this way can be achieved the same.
<?php
$associative_array = ['firstname' => 'John', 'lastname' => 'Smith', 'DOB' => '2000-10-10', 'country' => 'Ireland' ];
$subset = array_intersect_key( $associative_array, array_flip( [ 'lastname', 'country' ] ) );
print_r( $subset );
// Outputs...
// Array ( [lastname] => Smith [country] => Ireland );

Related

How to get values of array 1 and array 2 based on array index of array3 in laravel/php? [duplicate]

I wrote this function to get a subset of an array. Does php have a built in function for this. I can't find one in the docs. Seems like a waste if I'm reinventing the wheel.
function array_subset($array, $keys) {
$result = array();
foreach($keys as $key){
$result[$key] = $array[$key];
}
return $result;
}
I always want this too. Like a PHP version of Underscore's pick.
It's ugly and counter-intuitive, but what I sometimes do is this (I think this may be what prodigitalson was getting at):
$a = ['foo'=>'bar', 'zam'=>'baz', 'zoo'=>'doo'];
// Extract foo and zoo but not zam
print_r(array_intersect_key($a, array_flip(['foo', 'zoo'])));
/*
Array
(
[foo] => bar
[zoo] => doo
)
*/
array_intersect_key returns all the elements of the first argument whose keys are present in the 2nd argument (and all subsequent arguments, if any). But, since it compares keys to keys, I use array_flip for convenience. I could also have just used ['foo' => null, 'zoo' => null] but that's even uglier.
array_diff_key and array_intersect_key are probably what you want.
There is no direct function I think in PHP to get a subset from an array1 with compare to another array2 where the values are the list of key name which we fetch.
Like: array_only($array1, 'field1','field2');
But this way can be achieved the same.
<?php
$associative_array = ['firstname' => 'John', 'lastname' => 'Smith', 'DOB' => '2000-10-10', 'country' => 'Ireland' ];
$subset = array_intersect_key( $associative_array, array_flip( [ 'lastname', 'country' ] ) );
print_r( $subset );
// Outputs...
// Array ( [lastname] => Smith [country] => Ireland );

php setting value in multi dimensinal array depending on array of indices

When gathering statistical data I need to address an array with a varying number of indices.
I have to add to $array[$ind1][$ind2] and to $array[$ind1][$ind2][$ind3][$ind4].
Is it possible to create a function like arrayAdd($number,$arrayOfIndices)
where in the first case $arrayOfIndices is [$ind,$ind2]
and the second case [$ind1,$ind2,$ind3,$ind4]?
I solved my problem to write it all out, depending of count of $arrayOfIndices.
Looking however to a more elegant and generic way.
<?php
function array_add(&$array, array $indices, $value) {
count($indices)==1
? $array[$indices[0]] = $value
: array_add($array[array_shift($indices)], $indices, $value);
}
$data = ['pig'=>'man'];
$indices = ['foo', 'bar', 'baz'];
array_add($data, $indices, 'bat');
var_export($data);
Output:
array (
'pig' => 'man',
'foo' =>
array (
'bar' =>
array (
'baz' => 'bat',
),
),
)

array_diff_assoc() or foreach()? Which is faster?

I have two arrays, for example $session and $post with 100+ values. I will compare the $post array values with $session array. If post is different then it will be taken to result array else not.
We can try this using array_diff_assoc($post, $session) and foreach(). Which one is faster?
For profiling, Phil has suggested a great way in his reply, but I will link it here too, just in case:
Simplest way to profile a PHP script
Practically, you need to know what each approach does. in array_diff_assoc, you are returning the difference between 2 collections, after comparing the key/value couples for each element. It will then return an array that contains the entries from array1 that are not present in array2 or array3, etc.
In a for each loop, you will need to hard code the same function (assuming that's what you need). You will need to take the first element, then look for the combination in your other arrays. If it matches your requirements, you will save it into your output array, or even print it directly.
Same principles apply, but then again, it will be up to profiling to determine the faster approach. Try doing so on a large number of big arrays, as the difference isn't noticeable at smaller scales.
I'll leave this as a stub/example, please edit, or use for profiling.
<?php
$before = [
'name' => 'Bertie',
'age' => '23'
];
$after = [
'name' => 'Harold',
'age' => '23',
'occupation' => 'Bus driver'
];
function changed_1($after, $before) {
return array_diff_assoc($after, $before);
}
function changed_2($after, $before) {
$changed = [];
foreach($after as $k => $v) {
if(isset($before[$k]) && $before[$k] !== $v)
$changed[$k] = $v;
if(!isset($before[$k]))
$changed[$k] = $v;
}
return $changed;
}
var_export(changed_1($after, $before));
var_export(changed_2($after, $before));
Output:
array (
'name' => 'Harold',
'occupation' => 'Bus driver',
)array (
'name' => 'Harold',
'occupation' => 'Bus driver',
)

Merge row data from multiple arrays

I have two arrays as shown below. I need to merge the content of the arrays so that I can get the structure as shown in the third array at last. I have checked array_merge but can't figure out the way this is possible. Any help appreciated. Thanks.
[
['gross_value' => '100', 'quantity' => '1'],
['gross_value' => '200', 'quantity' => '1']
]
and
[
['item_title_id' => '1', 'order_id' => '4'],
['item_title_id' => '2', 'order_id' => '4']
];
I should get a merged array like this:
[
[
'gross_value' => '100',
'quantity' => '1',
'item_title_id' => '1',
'order_id' => 4
],
[
'gross_value' => '200',
'quantity' => '1',
'item_title_id' => '2',
'order_id' => 4
]
]
Use array_merge_recursive :
Convert all numeric key to strings, (make is associative array)
$result = array_merge_recursive($ar1, $ar2);
print_r($result);
See live demo here
how about:
$arr1 = array(
0 => array(
'gross_value' => '100',
'quantity' => '1'
),
1 => array(
'gross_value' => '200',
'quantity' => '1'
)
);
$arr2 = array(
0 => array(
'item_title_id' => '1',
'order_id' => '4'
),
1 => array(
'item_title_id' => '2',
'order_id' => '4'
)
);
$arr = array();
foreach($arr1 as $k => $v) {
array_push($arr, array_merge($v, $arr2[$k]));
}
print_r($arr);
output:
Array
(
[0] => Array
(
[gross_value] => 100
[quantity] => 1
[item_title_id] => 1
[order_id] => 4
)
[1] => Array
(
[gross_value] => 200
[quantity] => 1
[item_title_id] => 2
[order_id] => 4
)
)
Have a look at array_merge
I would probably iterate over the arrays and merge them manually.
$result = array();
foreach ( $array1 as $key => $item )
{
$result[$key] = array_merge($array1[$key], $array2[$key]);
}
You will have an issue if the top-level arrays don't have strictly matching keys though.
If you have $array1 and $array2, try this:
foreach($array1 as $key1=>$innerArray){
$array1[$key1]['item_title_id'] = $array2[$key1]['item_title_id'];
$array1[$key1]['order_id'] = $array2[$key1]['order_id'];
}
The problem with things like merge recursive is that they don't know when to stop.
In some scenarios you want to stop traversing down an array and simply take a given value if it exists.
For instance if you have to override a nested config array you might not want the default keys to stick around at a a specific level.
here is my solution:
public static function merge_lvl2(){
$args = func_get_args();
return static::merge($args, 2);
}
public static function merge($args, $maxDepth = null, $depth = 1)
{
$merge = [];
foreach($args as $arg) {
if (is_array($arg)) {
if (is_array($merge)) {
if ($maxDepth == $depth) {
$arg += $merge;
$merge = $arg;
} else {
$merge = array_merge($merge, $arg);
}
} else {
$merge = $arg;
}
}
}
if ($maxDepth !== $depth) {
foreach($args as $a) {
if (is_array($a)) {
foreach($a as $k => $v) {
if (isset($merge[$k]) && is_array($merge[$k])) {
$merge[$k] = static::merge([$merge[$k], $v], $maxDepth, $depth + 1);
}
}
}
}
}
return $merge;
}
You can pass as many arrays to merge as you want to.
$merged = ClassName::merge_lvl2([..array1..], [..array2..], [..array3..], etc...);
It will stop merging at level 2 and accept the last instance of the key as an override instead of a merge.
You can also call merge directly with an array of args and setting the max depth.
If no max depth is set it will traverse the entire array.
The most modern, elegant, concise way to merge rows from two or more arrays (or the rows from a multidimensional array with 3 or more levels of depth) is to call array_merge() on each row (array_replace() can also be used). array_map() can call array_merge by its string name and the input data can be split into individual arguments with the "spread operator" (...) when needed.
Code for the OP's arrays: (Demo)
var_export(
array_map('array_merge', $arr1, $arr2)
);
The above technique will return a newly indexed array (though you might not notice because the sample input arrays were indexed to begin with). If your input data has associative first-level keys, they will be ignored and destroyed by this technique. If you have non-numeric first-level keys and want to merge on those, then array_merge_recursive() is likely to be the ideal native function - Demo.
However, it must be said, that for the OP's sample data array_merge_recursive() IS NOT a correct technique.
My first snippet is conveniently extended if you have more than two arrays which need their rows to be merge based on their positions. (Demo)
var_export(
array_map('array_merge', $arr1, $arr2, $arr3)
);
And as mentioned earlier, the spread operator can be used to unpack deeper arrays with the same result. Again, the number of subarrays containing rows can be dynamic. If your deep array only has one subarray containing rows, then the result will be a "flattening" effect where the top level is removed.
Code with new data structure: (Demo)
$masterArray = [
[
['gross_value' => '100', 'quantity' => '5'],
['gross_value' => '200', 'quantity' => '6']
],
[
['item_title_id' => '1', 'order_id' => '3'],
['item_title_id' => '2', 'order_id' => '4']
],
[
['foo' => 'bar1'],
['foo' => 'bar2']
]
];
var_export(
array_map('array_merge', ...$masterArray)
);
To be fair, array_replace_recursive() does provide the desired result using the OP's sample data, but I find the technique to be semantically misleading. (Demo)
All that said, you are not forced to use PHP's native array functions; you can use classic loops as well -- you will have several ways to "unite" the rows inside the loop. This approach is a little less D.R.Y. because you need to explicitly specific the separate arrays that you wish to synchronously iterate. Just make sure that you understand the nuanced differences in using array_merge(), array_replace(), and the union operator (+) with different qualities of data. Hint: associative, non-numeric keyed data in the respective rows will be affected when key collisions occur. Be careful to use a merging technique that will not overwrite the wrong data when rows share associative keys.
array_merge() to build a new array: (array_merge() Demo) (array_replace() Demo) (array union operator + Demo)
$result = [];
foreach ($arr1 as $i => $row) {
$result[] = array_merge($row, $arr2[$i]);
}
var_export($result);
Alternative, you can use the same general approach, but instead of populating a new $result array, you can merge data into the first array.
(array_merge() Demo) (array_replace() Demo) (array union assignment operator += Demo)
foreach ($arr1 as $i => &$row) {
$row = array_merge($row, $arr2[$i]);
}
var_export($arr1);
For the sake of completeness, if you have an indexed array of indexed arrays, then you might even use iterated calls of array_push() with the spread operator to achieve a similar functionality. This quite literally, indicatively appends the row data from subsequent arrays to the first array's rows.
Code: (Demo)
$arr1 = [
['A', 'B', 'C'],
['F', 'G']
];
$arr2 = [
['D', 'E'],
['H', 'I', 'J', 'L']
];
foreach ($arr1 as $i => &$row) {
array_push($row, ...$arr2[$i]);
}
var_export($arr1);
Related non-duplicate content on Stack Overflow:
Partially merge one array's row data with another another array:Add column of values from one array to another
Merge two flat arrays to create an array of merged rows:Transforming array values in elements of a subarray using PHP
Merge rows with indexed elements, remove duplicates and reindex:Merge two multidimensional arrays, preserve numeric keys, and combine values inside array
Merge arrays containing objects:Merge rows of two arrays containing objects by first level index
Push single elements from one array to rows in another array:Push elements from one array into rows of another array (one element per row)
On the above pages, the rabbit hole goes further because I've linked other related pages to them. Keep researching until you find what you need.
If you are using Laravel, you might be interested in its combine() and collect() methods.

Reorganize an array by 'id' index of nested arrays

I have an array that looks like this:
Array([0]=>Array([id]=>7 [name]=foo) [1]=>Array([id]=>10 [name]=bar) [2]=>Array([id]=>15 [name]=baz))
Each index contains an another array with various elements including an 'id'. I would like to "go up" a level, such that my top-level array is indexed by the ID element of the corresponding nested arrays, but that index still contains an array with all of the elements that were in the sub arrays?
In other words, how can I use PHP to turn the above array into this:
Array([7]=>Array([id]=>7 [name]=foo) [10]=>Array([id]=>10 [name]=bar) [15]=>Array([id]=>15 [name]=baz))
What you need to do here is extract the ids from each sub-array in your input. If you have these as an array of ids, you are just an array_combine call away from re-indexing your original array to use these ids as the keys.
You can produce such an array of ids using array_map, which leads to:
// input data
$array = array(array('id' => '7', 'name' => 'foo'),array('id' => 10, 'name' => 'bar'));
// extract ids from the input array
$ids = array_map(function($arr) { return $arr['id']; }, $array);
// "reindex" original array using ids as array keys, keep original values
$result = array_combine($ids, $array);
print_r($result);
The syntax I 've used for the anonymous function (first argument to array_map) requires PHP >= 5.3, but you can achieve the same (although a bit less conveniently) with create_function in any PHP version you 'd not be ashamed of using.
See it in action.
In modern, supported versions of PHP, this whole task can be achieved with array_column() alone.
Using null as the second parameter will leave the rows unchanged.
Using id as the 3rd parameter will assign those columnar values as the new first level keys. Be aware that if these columnar values are not unique, subsequently encountered duplicates will overwrite previously encountered rows with the same id value -- this is because keys cannot be duplicates on a given level in an array.
DO NOT bother calling array_combine(), it is simply unnecessary/indirect.
Code: (Demo)
$array = [
['id' => 7, 'name' => 'foo'],
['id' => 10, 'name' => 'bar'],
['id' => 15, 'name' => 'baz'],
];
var_export(
array_column($array, null, 'id')
);
Output:
array (
7 =>
array (
'id' => 7,
'name' => 'foo',
),
10 =>
array (
'id' => 10,
'name' => 'bar',
),
15 =>
array (
'id' => 15,
'name' => 'baz',
),
)
Try this:
$newArray = array();
foreach($oldArray as $key => $value) {
$newArray[$value['id']] = $value;
}
Since PHP 5.5.0, you can shorten the code by using array_column() instead of array_map().
$result = array_combine(array_column($array, 'id'), $array);

Categories