I have $fruits_arr
:
Array
(
[0] => Array
(
[id] => 213
[fruit] => banana
)
[1] => Array
(
[id] => 438
[fruit] => apple
)
[2] => Array
(
[id] => 154
[fruit] => peach
)
)
And $ids_arr
:
Array (
[0] => 213
[1] => 154
)
I want to recreate $fruits_arr
to have only array items where id
is equal to a value from $ids_arr
. I also want to maintain the index/array order of $fruits_arr
.
I'm using the following:
$selected_fruits = array();
foreach( $fruits_arr as $fruit ) :
if ( in_array( $fruit['id'], $ids_arr ) ) :
$selected_fruits[] = $fruit;
endif;
endforeach;
print_r( $selected_fruits );
It seems to work but I am wondering if there is a shorter, better way to accomplish this in the latest PHP version.
3 Answers 3
Only thing you can use besides the foreach loop, it is array_filter
:
$selected_fruits = array_filter($fruits_arr, function($fruit) use($ids_arr) {
return in_array( $fruit['id'], $ids_arr );
});
looks shorter and uses native functions
-
\$\begingroup\$
ARRAY_FILTER_USE_BOTH
is entirely unnecessary. \$\endgroup\$mickmackusa– mickmackusa2019年07月15日 08:06:46 +00:00Commented Jul 15, 2019 at 8:06 -
\$\begingroup\$ yeap u are right \$\endgroup\$N1Creator– N1Creator2019年07月15日 16:42:29 +00:00Commented Jul 15, 2019 at 16:42
The best performing techniques for filtering arrays employ key-based comparisons.
To prepare your posted data structures for such a technique will require both arrays to have there ids copied to the first level keys.
Once both arrays have id values for keys, you can filter by comparison.
Code: (Demo)
$fruits_arr = [
['id' => 213, 'fruit' => 'banana'],
['id' => 438, 'fruit' => 'apple'],
['id' => 154, 'fruit' => 'peach']
];
$ids_arr = [213, 154];
var_export(array_intersect_key(array_column($fruits_arr, null, "id"), array_flip($ids_arr)));
This is theoretic efficiency, because more effort is required simply to prepare. It would be better if you could declare these data structures in the necessary structure.
All of the above assumes your ids are unique, if not your data will lose values with duplicated ids.
There's nothing wrong with your foreach loop, but you can use a slightly different syntax:
$selected_fruits = [];
foreach ($fruits as $fruit) {
if (in_array($fruit["id"], $selected_ids)) {
$selected_fruits[] = $fruit;
}
}
print_r($selected_fruits);
I've changed $fruits_arr
to $fruits
, and $ids_arr
to $selected_ids
. A variable name should describe the meaning or purpose of the data or resource a variable contains. And you should be consistent about your choices. For instance, why don't the arrays $selected_fruits
and $fruit
have the _arr
post-fix in your code? They are arrays as well.
There's no difference in the way the code above works, when compared with your code, but it is actually shorter than when array_filter()
is used. Shorter code is, however, never a hallmark of better code. I think readability, and simply code that makes sense, are far more important. Using a rather complex function like array_filter()
here is, in my eyes, overkill.
As far as I can tell the syntax, as used in this answer, is used more often than the syntax in your question. For the []
array declaration you need PHP 5.4 or higher.
You also wrote:
I also want to maintain the index/array order of $fruits_arr.
It is not completely clear what this means. If you actually want to maintain the same indexes you have to do a bit more. The code would then look like this:
$selected_fruits = [];
foreach ($fruits as $index => $fruit) {
if (in_array($fruit["id"], $selected_ids)) {
$selected_fruits[$index] = $fruit;
}
}
print_r($selected_fruits);
This would make the indexes of $selected_fruits
equal to those of $fruits_arr
.
It is clear that both foreach
loops, used in the code segments above, will have to walk the whole $fruits
array to find all selected fruits. If the $fruits
array becomes very long, compared to the number of selected fruits, this will not be very efficient. One of the reasons for this is the way that you've defined the $fruits_arr
. The keys in this array don't do much. If I assume that the fruit ids in this array are unique, then it would be very helpful if the $fruits_arr
keys actually were those fruit ids. I can redefine your $fruits_arr
to achieve this, with this code:
$fruits_arr = array_combine(array_column($fruits_arr, "id"), $fruits_arr);
Now the $fruits_arr
looks like this:
Array
(
[213] => Array
(
[id] => 213
[fruit] => banana
)
[438] => Array
(
[id] => 438
[fruit] => apple
)
[154] => Array
(
[id] => 154
[fruit] => peach
)
)
It would, of course, be better to have done this right from the start, so you don't have to manipulate $fruits_arr
to get those useful keys. Given this new array you can now do:
$selected_fruits = [];
foreach ($selected_ids as $fruit_id) {
$selected_fruits[] = $fruits[$fruit_id];
}
print_r($selected_fruits);
This code only walks over the shorter $selected_ids
array. It also doesn't contain the in_array()
function anymore, because I assumed that all selected fruits are present in the fruits array. It wouldn't be a selection otherwise.
Finally, the code above can be shortened to:
$selected_fruits = array_intersect_key($fruits, array_flip($selected_ids));
print_r($selected_fruits);
This is the shortest and most efficient version I can think of.
-
\$\begingroup\$ Don't use combine(), see my snippet. \$\endgroup\$mickmackusa– mickmackusa2019年07月15日 08:21:45 +00:00Commented Jul 15, 2019 at 8:21
-
\$\begingroup\$ And flip the selected ids \$\endgroup\$mickmackusa– mickmackusa2019年07月15日 08:33:50 +00:00Commented Jul 15, 2019 at 8:33
-
\$\begingroup\$ I agree that
combine()
should not be used, it's better to create the array in the correct form from the start. And you're right about thearray_intersect_key()
, I'll correct that. \$\endgroup\$KIKO Software– KIKO Software2019年07月15日 09:21:27 +00:00Commented Jul 15, 2019 at 9:21
$fruits_arr
array?in_array()
is not an efficient means to filter your data. \$\endgroup\$