Reputation: 27
I have made the following using standard PHP coding, involving loops and things. Though, I am sure I can accomplish the same algorithm using a combination of all PHP array functions: array_intersect*, array_fill*, and co...
I am hoping someone already encountered this and found a way, a fast way.
Ok, here it is:
I have an indexed array, let's call it data, it contains loads of informations about an entity, order doesn't matter obviously as it is an indexed array after all:
$data = [
'title' => 'loops and things',
'id' => '28fr2',
'link' => 'https://blah',
'itemid' => '28fr3',
'quantity' => 3,
//...
];
Now, I have a set of other arrays which describe a subset of my data array, for instance:
$product = [
'id',
'title',
'link'
];
and
$inventory = [
'itemid',
'quantity',
];
what I am after is something like:
array_intersect_key_while_we_keep_the_order($data, $product);
would give
[
'id' => '28fr2',
'title' => 'loops and things',
'link' => 'https://blah'
]
and
array_intersect_key_while_we_keep_the_order($data, $inventory);
would give
[
'itemid' => '28fr2',
'quantity' => 3
]
The problem I have with array_intersect_key is that the "order" is from the source array, not the one defining the list of keys. This is obviously straight forward using foreaches, but I am sure it can be done faster. I just can't see it.
Upvotes: 0
Views: 2413
Reputation: 21671
Pretty simple
$data = [
'title' => 'loops and things',
'id' => '28fr2',
'link' => 'https://blah',
'itemid' => '28fr3',
'quantity' => 3,
//...
];
$product = [
'id',
'title',
'link'
];
$res = array_merge(array_fill_keys($product, null),array_intersect_key($data, array_flip($product)));
print_r($res);
Output
Array
(
[id] => 28fr2
[title] => loops and things
[link] => https://blah
)
How it works, The main trick here is array fill keys, and array merge. By using these we can create a "template" array. Because merging 2 arrays with the same keys merge in the order of the first array. So we can setup that first array to be the order of $product
and then when we merge into that the "fake" values are replaced with the real values and the order is preserved.
If you wanted to you could also use the result of the array flip (or array fill keys), but then it would be 2 lines of code... :)
And of course we have to use something like array flip or array fill keys to change the values of $product
into the keys we can use for array intersect key.
I used both array fill keys and array flip for variety, in this situation they are somewhat interchangeable. For example this is a bit more efficient:
$map = array_fill_keys($product, null);
$res = array_merge($map,array_intersect_key($data, $map));
print_r($res);
But then it takes 2 lines... I should mention array fill keys is preferred to array flip. It doesn't really matter because the array intersect key filters anything out not in map. But it's "in general" better because then the values of the map array are the same, whereas when using flip they are the numeric keys which can be a bit harder to work with for some situations (but not this one).
I almost for got you can functionize it like this
function ordered_array_intersect_key(array $data, array $map){
return array_merge(($map = array_fill_keys($map, null)),array_intersect_key($data, $map));
}
I took the liberty of naming it something sensible and by using assignment inline in array_merge
you can have your cake and eat it too (reuse array_fill_keys and have 1 line). Something I just happened to think of when making a function out of it.
Enjoy!
UPDATE
Thanks to mosh442, for the idea. You can also use array_walk
to build a new array like this:
$data = [
'title' => 'loops and things',
'id' => '28fr2',
'link' => 'https://blah',
'itemid' => '28fr3',
'quantity' => 3,
//...
];
$product = [
'id',
'title',
'link'
];
$new = [];
array_walk($product, function($v,$k,$data) use (&$new){$new[$v] = $data[$v];},$data);
print_r($new);
Output
Array
(
[id] => 28fr2
[title] => loops and things
[link] => https://blah
)
I love questions like this, so much fun....
Upvotes: 2
Reputation: 738
You can also use array_filter
array_filter(
$data,
function ($key) use ($inventory) {return in_array($key,$inventory);} ,
ARRAY_FILTER_USE_KEY
);
As noted in the comments the ARRAY_FILTER_USE_KEY flag was added in PHP 5.6.0
Upvotes: 1