0

I have an multidimensional array and i'd like to filter by many values.

$datas = [
    [
        'id' => 2135,
        'first_name' => 'John',
        'last_name' => 'Doe',
        'gender' => 'male'
    ],
    [
        'id' => 3245,
        'first_name' => 'Sally',
        'last_name' => 'Smith',
        'gender' => 'female'
    ],
    [
        'id' => 5342,
        'first_name' => 'Jane',
        'last_name' => 'Doe',
        'gender' => 'female'
    ],
    [
        'id' => 5623,
        'first_name' => 'Peter',
        'last_name' => 'Doe',
        'gender' => 'male'
    ],
    [
        'id' => 7216,
        'first_name' => 'Mike',
        'last_name' => 'Lill',
        'gender' => 'male'
    ]
];

I have combined the methods of array_filter and array_search and got a good result but this only does the filtering of only one value. I'd like something like this...

array_filter(
    $datas,
    function ($key) {
        return array_search(['Doe', 'male'...], $key);
});

And its give

array(
    array(
        'id' => 2135,
        'first_name' => 'John',
        'last_name' => 'Doe',
        'gender' => 'male'
    ),
    array(
        'id' => 5623,
        'first_name' => 'Peter',
        'last_name' => 'Doe',
        'gender' => 'male'
    )
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Jefter Rocha
  • 387
  • 2
  • 14

2 Answers2

4

Of course you can simply loop any given input and check if a given element matches any criteria you implement:

<?php
$input = [
    [
        'id' => 2135,
        'first_name' => 'John',
        'last_name' => 'Doe',
        'gender' => 'male'
    ],
    [
        'id' => 3245,
        'first_name' => 'Sally',
        'last_name' => 'Smith',
        'gender' => 'female'
    ],
    [
        'id' => 5342,
        'first_name' => 'Jane',
        'last_name' => 'Doe',
        'gender' => 'female'
    ],
    [
        'id' => 5623,
        'first_name' => 'Peter',
        'last_name' => 'Doe',
        'gender' => 'male'
    ],
    [
        'id' => 7216,
        'first_name' => 'Mike',
        'last_name' => 'Lill',
        'gender' => 'male'
    ]
];

$needles = ['Doe', 'male'];
$output = [];
array_walk($input, function($element) use ($needles, &$output) {
    $matches = true;
    foreach ($needles as $needle) {
        if (!in_array($needle, $element)) {
            $matches = false;
        }
    }
    if ($matches) {
        $output[] = $element;
    }
});

print_r($output);

The obvious output is:

Array
(
    [0] => Array
        (
            [id] => 2135
            [first_name] => John
            [last_name] => Doe
            [gender] => male
        )

    [1] => Array
        (
            [id] => 5623
            [first_name] => Peter
            [last_name] => Doe
            [gender] => male
        )

)
arkascha
  • 41,620
  • 7
  • 58
  • 90
2

You wish to filter your multidimensional array based on multiple qualifying values irrespective of their keys.

If $datas is coming from a database, you should be performing this filtration in your sql to avoid needlessly squandering resources.

If you must filter in php, then array_filter() is an intuitive and logical function to call upon.

Code: (Demo)

$needles = ['Doe', 'male'];

var_export(
    array_filter($datas, function($row) use ($needles) {
        return !array_diff($needles, $row);
    })
);

array_filter() processes a boolean value in each return as it iterates the rows. array_intersect($needles, $row) will only keep the $needles elements which are present in the current row. If the original $needles contains the same data as the filtered $needles then return true (keep the row).

From PHP7.4 and higher, you can use arrow syntax:

var_export(
    array_filter($datas, fn($row) => !array_diff($needles, $row)
);

Output: (from either snippet)

array (
  0 => 
  array (
    'id' => 2135,
    'first_name' => 'John',
    'last_name' => 'Doe',
    'gender' => 'male',
  ),
  3 => 
  array (
    'id' => 5623,
    'first_name' => 'Peter',
    'last_name' => 'Doe',
    'gender' => 'male',
  ),
)

Note that the original first-level keys are preserved. If your project requires the multidimensional array to be reindexed, write the array_filter() call inside of array_values().


If you'd like some other techniques for the sake of running your own benchmarks with your own data, these language constructs will also provide filtration: (Demo)

foreach ($datas as $row) {
    if (!array_diff($needles, $row)) { // all needles found
        $result[] = $row;
    }
}
var_export($result);  // reindexed already

and

foreach ($datas as $index => $row) {
    if (array_diff($needles, $row)) { // one or more needles not found
        unset($datas[$index]);
    }
}
var_export($datas);

For best efficiency, implement a nested loop with in_array() calls. It may seem counterintuitive to make so many iterated function calls, but the conditional continue allows the time complexity to be optimized and therefore delivers a better "little o" because it can stop iterating as soon as a needle is not found.

Code: (Demo) (array_filter() Demo)

foreach ($datas as $row) {
    foreach ($needles as $needle) {
        if (!in_array($needle, $row)) {
            continue 2;
        }
    }
    $result[] = $row;
}
var_export($result);

I should also point to this related page (which has an unfortunately vague problem statement). I do not endorse a few of the techniques there (including the accepted answer), but there is certainly a technical relationship between these two pages.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136