按第二个数组中的值过滤数组

** I have edited this to show how I got my code to work using array_search

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 );
} );

View this example on eval.in


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_column extracts the value of the id column into an array
  • array_intersect returns the elements which are in both $arr1['id'] and $arr2
  • 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]);;
    }