I have an array, $arr1
with 5 columns as such:
key id name style age whim
0 14 bob big 33 no
1 72 jill big 22 yes
2 39 sue yes 111 yes
3 994 lucy small 23 no
4 15 sis med 24 no
5 16 maj med 87 yes
6 879 Ike larg 56 no
7 286 Jed big 23 yes
This array is in a cache, not a database.
I then have a second array with a list of id values -
$arr2 = (0=>14, 1=>72, 2=>8790)
How do I filter $arr1
so it returns only the rows with the id values in $arr2
?
I got my code to work as follows:
$arr1 = new CachedStuff(); // get cache
$resultingArray = []; // create an empty array to hold rows
$filter_function = function ($row) use ($arr2) {
return (array_search($row['id'], $arr2));
};
$resultingArrayIDs = $arr1->GetIds($filter_function, $resultingArray);
This gives me two outputs: $resultingArray & $resultingArrayIDs both of which represent the intersection of the $arr1 and $arr2.
The trouble with using iterated checks on the second/filtering array is that it will lose efficiency as the arrays' sizes increase.
By assigning new keys to the first array, and flipping the filtering array's values to keys, you run a one-time filter/intersect on the arrays and then reindex the output array.
My method calls no foreach()
loops and has no iterated conditionals.
Code: (Demo)
$arr1=[
['key'=>0,'id'=>14,'name'=>'bob','style'=>'big','age'=>33,'whim'=>'no'],
['key'=>1,'id'=>72,'name'=>'jill','style'=>'big','age'=>22,'whim'=>'yes'],
['key'=>2,'id'=>39,'name'=>'sue','style'=>'yes','age'=>111,'whim'=>'yes'],
['key'=>3,'id'=>994,'name'=>'lucy','style'=>'small','age'=>23,'whim'=>'no'],
['key'=>4,'id'=>15,'name'=>'sis','style'=>'med','age'=>24,'whim'=>'no'],
['key'=>5,'id'=>16,'name'=>'maj','style'=>'med','age'=>87,'whim'=>'yes'],
['key'=>6,'id'=>879,'name'=>'Ike','style'=>'larg','age'=>56,'whim'=>'no'],
['key'=>7,'id'=>286,'name'=>'Jed','style'=>'big','age'=>23,'whim'=>'yes']
];
$arr2=[14,72,879];
$arr1=array_column($arr1,null,'id'); // generate temporary keys
//var_export($arr1);
$arr2=array_flip($arr2); // move values to keys
//var_export($arr2);
$filtered=array_intersect_key($arr1,$arr2); // retain subarrays that qualify
//var_export($filtered);
$reindexed=array_values($filtered); // reindex array
var_export($reindexed);
or written as a one-liner like this:
var_export(array_values(array_intersect_key(array_column($arr1,null,'id'),array_flip($arr2))));
Output:
array (
0 =>
array (
'key' => 0,
'id' => 14,
'name' => 'bob',
'style' => 'big',
'age' => 33,
'whim' => 'no',
),
1 =>
array (
'key' => 1,
'id' => 72,
'name' => 'jill',
'style' => 'big',
'age' => 22,
'whim' => 'yes',
),
2 =>
array (
'key' => 6,
'id' => 879,
'name' => 'Ike',
'style' => 'larg',
'age' => 56,
'whim' => 'no',
),
)
Something like this should do it, provided I've understood your question and data structure correctly:
$dataArray = [
[ 'key' => 0, 'id' => 14 , 'name' => 'bob' , 'style' => 'big' , 'age' => 33 , 'whim' => 'no' ],
[ 'key' => 1, 'id' => 72 , 'name' => 'jill' , 'style' => 'big' , 'age' => 22 , 'whim' => 'yes' ],
[ 'key' => 2, 'id' => 39 , 'name' => 'sue' , 'style' => 'yes' , 'age' => 111 , 'whim' => 'yes' ],
[ 'key' => 3, 'id' => 994 , 'name' => 'lucy' , 'style' => 'small' , 'age' => 23 , 'whim' => 'no' ],
[ 'key' => 4, 'id' => 15 , 'name' => 'sis' , 'style' => 'med' , 'age' => 24 , 'whim' => 'no' ],
[ 'key' => 5, 'id' => 16 , 'name' => 'maj' , 'style' => 'med' , 'age' => 87 , 'whim' => 'yes' ],
[ 'key' => 6, 'id' => 879 , 'name' => 'Ike' , 'style' => 'larg' , 'age' => 56 , 'whim' => 'no' ],
[ 'key' => 7, 'id' => 286 , 'name' => 'Jed' , 'style' => 'big' , 'age' => 23 , 'whim' => 'yes' ]
];
$filterArray = [14, 72, 879];
$resultArray = array_filter( $dataArray, function( $row ) use ( $filterArray ) {
return in_array( $row[ 'id' ], $filterArray );
} );
However, your question appears to suggest this data might be coming from a database; is that correct? If so, perhaps it's more efficient to pre-filter the results at the database-level. Either by adding a field in the SELECT query, that represents a boolean value whether a row matched your filter ids, or by simply not returning the other rows at all.
One way is with foreach
loop with array_search()
$result = [];
foreach ($arr1 as $value) { // Loop thru $arr1
if (array_search($value['id'], $arr2) !== false) { // Check if id is in $arr2
$result[] = $value; // Push to result if true
}
}
// print result
print_r($result);
As @DecentDabbler mentioned - if the data is coming out of a database, using an IN on your WHERE will allow you to retrieve only the relevant data.
Another way to filter is to use array functions
array_flip flips the resulting array such that the indices into $arr1 indicate the elements in both $arr1 and $arr2
$arr1 = [ [ 'id' => 14, 'name' => 'bob'],
['id' => 72, 'name' => 'jill'],
['id' => 39, 'name' => 'sue'],
['id' => 994, 'name' => 'lucy'],
['id' => 879, 'name'=> 'large']];
$arr2 = [ 14,72,879 ];
$intersection = array_flip(array_intersect(array_column($arr1,'id'),$arr2));
foreach ($intersection as $i) {
var_dump($arr1[$i]);;
}