Use PHP's array_filter function with a callback.
$new = array_filter($arr, function ($var) {
return ($var['name'] == 'CarEnquiry');
});
Edit: If it needs to be interchangeable, you can modify the code slightly:
$filterBy = 'CarEnquiry'; // or Finance etc.
$new = array_filter($arr, function (
filterBy) {
return ($var['name'] == $filterBy);
});
Answer from dchesterton on Stack OverflowUse PHP's array_filter function with a callback.
$new = array_filter($arr, function ($var) {
return ($var['name'] == 'CarEnquiry');
});
Edit: If it needs to be interchangeable, you can modify the code slightly:
$filterBy = 'CarEnquiry'; // or Finance etc.
$new = array_filter($arr, function (
filterBy) {
return ($var['name'] == $filterBy);
});
If you want to make this a generic function use this:
function filterArrayByKeyValue($array,
keyValue)
{
return array_filter($array, function($value) use (
keyValue) {
return $value[
keyValue;
});
}
Helllo ! I would like to use filter to iterate through an Array of arrays ! and to sort items which have gender = "male" ! I already used foreach loop and it works super easy by pointing the item of items item['gender']..But How can I filter a multidimensional array!
$items = [ ['name' => "Toto","age" => 47,"gender" => "Male" ], ['name' => "titi","age" => 12,"gender" => "Male" ], ['name' => "tata","age" => 13,"gender" => "Female" ]];I used this snippet$new = array_filter($items, function ($var) {return ($var['sex'] == 'Male');});echo $new;This snippest occurs the following error: Uncaught Error: Value of type null is not callable ........... Stack trace: #0 {main} thrown in
I decided to rewrite my answer to accommodate both filtering and sorting. I took a heavily object oriented approach to solving this problem, which I will detail below.
You can see all of this code in action at this ideone.com live demonstration.
The first thing I did was define two interfaces.
interface Filter {
public function filter($item);
}
interface Comparator {
public function compare($a, $b);
}
As their names suggest, Filter is used for filtering, and Comparator is used for comparing.
Next, I defined three concrete classes that implements these interfaces, and accomplish what I wanted.
First is KeyComparator. This class simply compares the key of one element to the key of another element.
class KeyComparator implements Comparator {
protected $direction;
protected $transform;
protected $key;
public function __construct($key, $direction = SortDirection::Ascending, $transform = null) {
$this->key = $key;
$this->direction = $direction;
$this->transform = $transform;
}
public function compare($a, $b) {
$a = $a[$this->key];
$b = $b[$this->key];
if ($this->transform) {
$a = $this->transform($a);
$b = $this->transform($b);
}
return $a === $b ? 0 : (($a > $b ? 1 : -1) * $this->direction);
}
}
You can specify a sort direction, as well as a transformation to be done to each element before they are compared. I defined a helped class to encapsulate my SortDirection values.
class SortDirection {
const Ascending = 1;
const Descending = -1;
}
Next, I defined MultipleKeyComparator which takes multiple KeyComparator instances, and uses them to compare two arrays against each other. The order in which they are added to the MultipleKeyComparator is the order of precedence.
class MultipleKeyComparator implements Comparator {
protected $keys;
public function __construct($keys) {
$this->keys = $keys;
}
public function compare($a, $b) {
$result = 0;
foreach ($this->keys as $comparator) {
if ($comparator instanceof KeyComparator) {
$result = $comparator->compare($a, $b);
if ($result !== 0) return $result;
}
}
return $result;
}
}
Finally, I created MultipleKeyValueFilter which is meant to filter an array based on an array of key/value pairs:
class MultipleKeyValueFilter implements Filter {
protected $kvPairs;
public function __construct($kvPairs) {
$this->kvPairs = $kvPairs;
}
public function filter($item) {
$result = true;
foreach ($this->kvPairs as $key => $value) {
if ($item[$key] !== $value)
$result &= false;
}
return $result;
}
}
Now, given the input array (Notice I rearranged them a bit to make the sorting obvious):
$array = array (
'1' => array ('type' => 'blah2', 'category' => 'cat2', 'exp_range' => 'this_week' ),
'2' => array ('type' => 'blah1', 'category' => 'cat1', 'exp_range' => 'this_week' ),
'3' => array ('type' => 'blah1', 'category' => 'cat2', 'exp_range' => 'next_week' ),
'4' => array ('type' => 'blah1', 'category' => 'cat1', 'exp_range' => 'next_week' )
);
Sorting can be achieved by doing the following:
$comparator = new MultipleKeyComparator(array(
new KeyComparator('type'),
new KeyComparator('exp_range')
));
usort($array, array($comparator, 'compare'));
echo "Sorted by multiple fields\n";
print_r($array);
Filtering can be achieved by doing the following:
$filter = new MultipleKeyValueFilter(array(
'type' => 'blah1'
));
echo "Filtered by multiple fields\n";
print_r(array_filter($array, array($filter, 'filter')));
At this point I've given you a great deal of code. I'd suggest that your next step is to combine these two pieces into a single class. This single class would then apply both filtering and sorting together.
Do it:
$arr = array(
1 => array ( "type" => "blah1", "category" => "cat1", "exp_range" => "this_week" ),
2 => array ( "type" => "blah1", "category" => "cat2", "exp_range" => "next week" ),
3 => array ( "type" => "blah1", "category" => "cat1", "exp_range" => "this_week" ),
4 => array ( "type" => "blah2", "category" => "cat2","exp_range" => "next week" ),
);
function filter(array $arr,array $params){
$out = array();
foreach($arr as $key=>$item){
$diff = array_diff_assoc($item,$params);
if (count($diff)==1) // if count diff == 1 - Ok
$out[$key] = $item;
}
return $out;
}
$out = filter($arr,array("type" => "blah1", "category" => "cat1"));
echo '<pre>';
print_r($out);
echo '</pre>';
// output
Array
(
[1] => Array
(
[type] => blah1
[category] => cat1
[exp_range] => this_week
)
[3] => Array
(
[type] => blah1
[category] => cat1
[exp_range] => this_week
)
)
The hint is already there, to filter, use array_filter.
Don't forget to use use keyword to import your criteria.
Example:
$newArray = array_filter($myArray, function($e) use ($filter){
// ^ import criteria
return in_array($e['name'], $filter);
});
$myArray = array(
array("name"=>"Andrea", "Age"=>17),
array("name"=>"Tresna", "Age"=>20),
array("name"=>"Aria", "Age"=>12)
);
$filter = array("Andrea", "Aria");
foreach($myArray as $arr)
{
foreach($filter as $value)
{
if(in_array($value,$arr))
{
$finalArr[]=$arr;
}
}
}
$finalArr is your result
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
)
)
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.