So I have the following array:
$array = array(array('fruit1' => 'apple'),
array('fruit2' => 'orange'),
array('veg1' => 'tomato'),
array('veg2' => 'carrot'));
and I want to run a function like this:
array_remove_recursive($array, 'tomato');
so the output is this:
$array = array(array('fruit1' => 'apple'),
array('fruit2' => 'orange'),
array('veg2' => 'carrot')); // note: the entire array containing tomato has been removed!
Is this solvable?
This will recursively unset the matching variable at any depth and then remove the parent element only if it is empty.
function recursive_unset(array &$array, $unwanted_val) {
foreach ($array as $key => &$value) {
if (is_array($value)) {
recursive_unset($value, $unwanted_val);
if(!count($value)){
unset($array[$key]);
}
} else if($value == $unwanted_val){
unset($array[$key]);
}
}
}
Function to remove recursively many values from multidimensional array.
# Example 1
$arr1 = array_remove_recursive($arr, 'tomato');
# Example 2
$arr2 = array_remove_recursive($arr, ['tomato', 'apple']);
function array_remove_recursive($arr, $values)
{
if (!is_array($values))
$values = [$values];
foreach ($arr as $k => $v) {
if (is_array($v)) {
if ($arr2 = array_remove_recursive($v, $values))
$arr[$k] = $arr2;
else
unset($arr[$k]);
} elseif (in_array($v, $values, true))
unset($arr[$k]);
}
return $arr;
}
function array_remove_recursive($getArray,$getAssocValue)
{
$returnArray = array();
if(is_array($getArray))
{
foreach($getArray as $indAssocValue)
{
if(is_array($indAssocValue))
{
foreach($indAssocValue as $innerKey=>$innerVal)
{
if($innerVal!=$getAssocValue and $innerKey!="")
{
$returnArray[] = array("$innerKey"=>$innerVal);
}
}
}
}
}
return $returnArray;
}
$array = array(array('fruit1' => 'apple'),
array('fruit2' => 'orange'),
array('veg1' => 'tomato'),
array('veg2' => 'carrot'));
print_r($array);
echo "<br />";
$array = array_remove_recursive($array, 'tomato');
print_r($array);
hope the above code would be helpfull.
Newbie question - I'm trying to remove elements with keys that start with 'not__'.
Its inside laravel project so I (can) use its array functions.
I'm trying to remove elements after the loop. This doesn't remove anything, i.e. it doesn't work:
function fixinput($arrinput)
{
$keystoremove = array();
foreach ($arrinput as $key => $value);
{
if (starts_with($key, 'not__'))
{
$keystoremove = array_add($keystoremove, '', $key);
}
}
$arrinput = array_except($arrinput, $keystoremove);
return $arrinput;
}
Note that it won't be the only task on array. I'll try that myself. :)
Thanks!
$filtered = array();
foreach ($array as $key => $value) {
if (strpos($key, 'not__') !== 0) {
$filtered[$key] = $value;
}
}
Using the array_filter function with the ARRAY_FILTER_USE_KEY flag looks to be the best/fastest option.
$arrinput = array_filter( $arrinput, function($key){
return strpos($key, 'not__') !== 0;
}, ARRAY_FILTER_USE_KEY );
The flag parameters weren't added until version 5.6.0 so for older versions of PHP a for loop would probably be the quickest option.
foreach ($arrinput as $key => $value) {
if(strpos($key, 'not__') === 0) {
unset($arrinput[$key]);
}
}
I would assume the following method is a lot slower but its just a different way of doing it.
$allowed_keys = array_filter( array_keys( $arrinput ), function($key){
return strpos($key, 'not__') !== 0;
} );
$arrinput = array_intersect_key($arrinput , array_flip( $allowed_keys ));
Function
if(!function_exists('array_remove_keys_beginning_with')){
function array_remove_keys_beginning_with( $array, $str ) {
if(defined('ARRAY_FILTER_USE_KEY')){
return array_filter( $array, function($key) use ($str) {
return strpos($key, $str) !== 0;
}, ARRAY_FILTER_USE_KEY );
}
foreach ($array as $key => $value) {
if(strpos($key, $str) === 0) {
unset($array[ $key ]);
}
}
return $array;
}
}
Some PHP-regex fu:
$array = array('not__foo' => 'some_data', 'bar' => 'some_data', 12 => 'some_data', 15 => 'some_data', 'not__taz' => 'some_data', 'hello' => 'some_data', 'not__world' => 'some_data', 'yeah' => 'some_data'); // Some data
$keys = array_keys($array); // Get the keys
$filter = preg_grep('#^not__#', $keys); // Get the keys starting with not__
$output = array_diff_key($array, array_flip($filter)); // Filter it
print_r($output); // Output
Output
Array
(
[bar] => some_data
[12] => some_data
[15] => some_data
[hello] => some_data
[yeah] => some_data
)
Try this...
function fixinput($arrinput)
{
$keystoremove = array();
foreach ($arrinput as $key => $value);
{
if (preg_match("#^not__#",$key))
{
$keystoremove = array_add($keystoremove, '', $key);
}
}
$arrinput = array_except($arrinput, $keystoremove);
return $arrinput;
}
I've got an array with nested arrays, and I was trying to use the *search_array* function to sift through the array and give me back their keys. It hasn't been working. Here's the code:
<?php
$array = array(
'cat1' => array(1,2,3),
'cat2' => array(4,5,6),
'cat3' => array(7,8,9),
);
foreach($array as $cat){
if(is_array($cat)
echo array_search(5,$cat); //want it to return 'cat2'
else
echo array_search(5,$array);
}
Thanks!
If you always have a two-dimensional array, then it is as easy as:
function find($needle, $haystack) {
foreach($haystack as $key=>$value){
if(is_array($value) && array_search($needle, $value) !== false) {
return $key;
}
}
return false;
}
$cat = find(5, $array);
function mySearch($haystack, $needle, $index = null)
{
$aIt = new RecursiveArrayIterator($haystack);
$it = new RecursiveIteratorIterator($aIt);
while($it->valid())
{
if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) {
return $aIt->key();
}
$it->next();
}
return false;
}
$array = array(
'cat1' => array(1,2,3),
'cat2' => array(4,5,6),
'cat3' => array(7,8,9),
);
echo $arr_key = mySearch($array, 5);
this will give u the answer
Is there any fast way to get all subarrays where a key value pair was found in a multidimensional array? I can't say how deep the array will be.
Simple example array:
$arr = array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>2,name=>"cat 2"),
2 => array(id=>3,name=>"cat 1")
);
When I search for key=name and value="cat 1" the function should return:
array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>3,name=>"cat 1")
);
I guess the function has to be recursive to get down to the deepest level.
Code:
function search($array, $key, $value)
{
$results = array();
if (is_array($array)) {
if (isset($array[$key]) && $array[$key] == $value) {
$results[] = $array;
}
foreach ($array as $subarray) {
$results = array_merge($results, search($subarray, $key, $value));
}
}
return $results;
}
$arr = array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>2,name=>"cat 2"),
2 => array(id=>3,name=>"cat 1"));
print_r(search($arr, 'name', 'cat 1'));
Output:
Array
(
[0] => Array
(
[id] => 1
[name] => cat 1
)
[1] => Array
(
[id] => 3
[name] => cat 1
)
)
If efficiency is important you could write it so all the recursive calls store their results in the same temporary $results array rather than merging arrays together, like so:
function search($array, $key, $value)
{
$results = array();
search_r($array, $key, $value, $results);
return $results;
}
function search_r($array, $key, $value, &$results)
{
if (!is_array($array)) {
return;
}
if (isset($array[$key]) && $array[$key] == $value) {
$results[] = $array;
}
foreach ($array as $subarray) {
search_r($subarray, $key, $value, $results);
}
}
The key there is that search_r takes its fourth parameter by reference rather than by value; the ampersand & is crucial.
FYI: If you have an older version of PHP then you have to specify the pass-by-reference part in the call to search_r rather than in its declaration. That is, the last line becomes search_r($subarray, $key, $value, &$results).
How about the SPL version instead? It'll save you some typing:
// I changed your input example to make it harder and
// to show it works at lower depths:
$arr = array(0 => array('id'=>1,'name'=>"cat 1"),
1 => array(array('id'=>3,'name'=>"cat 1")),
2 => array('id'=>2,'name'=>"cat 2")
);
//here's the code:
$arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));
foreach ($arrIt as $sub) {
$subArray = $arrIt->getSubIterator();
if ($subArray['name'] === 'cat 1') {
$outputArray[] = iterator_to_array($subArray);
}
}
What's great is that basically the same code will iterate through a directory for you, by using a RecursiveDirectoryIterator instead of a RecursiveArrayIterator. SPL is the roxor.
The only bummer about SPL is that it's badly documented on the web. But several PHP books go into some useful detail, particularly Pro PHP; and you can probably google for more info, too.
<?php
$arr = array(0 => array("id"=>1,"name"=>"cat 1"),
1 => array("id"=>2,"name"=>"cat 2"),
2 => array("id"=>3,"name"=>"cat 1")
);
$arr = array_filter($arr, function($ar) {
return ($ar['name'] == 'cat 1');
//return ($ar['name'] == 'cat 1' AND $ar['id'] == '3');// you can add multiple conditions
});
echo "<pre>";
print_r($arr);
?>
Ref: http://php.net/manual/en/function.array-filter.php
Came back to post this update for anyone needing an optimisation tip on these answers, particulary John Kugelman's great answer up above.
His posted function work fine but I had to optimize this scenario for handling a 12 000 row resultset. The function was taking an eternal 8 secs to go through all records, waaaaaay too long.
I simply needed the function to STOP searching and return when match was found. Ie, if searching for a customer_id, we know we only have one in the resultset and once we find the customer_id in
the multidimensional array, we want to return.
Here is the speed-optimised ( and much simplified ) version of this function, for anyone in need. Unlike other version, it can only handle only one depth of array, does not recurse and does away with merging multiple results.
// search array for specific key = value
public function searchSubArray(Array $array, $key, $value) {
foreach ($array as $subarray){
if (isset($subarray[$key]) && $subarray[$key] == $value)
return $subarray;
}
}
This brought down the the task to match the 12 000 records to a 1.5 secs. Still very costly but much more reasonable.
if (isset($array[$key]) && $array[$key] == $value)
A minor imporvement to the fast version.
Here is solution:
<?php
$students['e1003']['birthplace'] = ("Mandaluyong <br>");
$students['ter1003']['birthplace'] = ("San Juan <br>");
$students['fgg1003']['birthplace'] = ("Quezon City <br>");
$students['bdf1003']['birthplace'] = ("Manila <br>");
$key = array_search('Delata Jona', array_column($students, 'name'));
echo $key;
?>
Be careful of linear search algorithms (the above are linear) in multiple dimensional arrays as they have compounded complexity as its depth increases the number of iterations required to traverse the entire array. Eg:
array(
[0] => array ([0] => something, [1] => something_else))
...
[100] => array ([0] => something100, [1] => something_else100))
)
would take at the most 200 iterations to find what you are looking for (if the needle were at [100][1]), with a suitable algorithm.
Linear algorithms in this case perform at O(n) (order total number of elements in entire array), this is poor, a million entries (eg a 1000x100x10 array) would take on average 500,000 iterations to find the needle. Also what would happen if you decided to change the structure of your multidimensional array? And PHP would kick out a recursive algorithm if your depth was more than 100. Computer science can do better:
Where possible, always use objects instead of multiple dimensional arrays:
ArrayObject(
MyObject(something, something_else))
...
MyObject(something100, something_else100))
)
and apply a custom comparator interface and function to sort and find them:
interface Comparable {
public function compareTo(Comparable $o);
}
class MyObject implements Comparable {
public function compareTo(Comparable $o){
...
}
}
function myComp(Comparable $a, Comparable $b){
return $a->compareTo($b);
}
You can use uasort() to utilize a custom comparator, if you're feeling adventurous you should implement your own collections for your objects that can sort and manage them (I always extend ArrayObject to include a search function at the very least).
$arrayObj->uasort("myComp");
Once they are sorted (uasort is O(n log n), which is as good as it gets over arbitrary data), binary search can do the operation in O(log n) time, ie a million entries only takes ~20 iterations to search. As far as I am aware custom comparator binary search is not implemented in PHP (array_search() uses natural ordering which works on object references not their properties), you would have to implement this your self like I do.
This approach is more efficient (there is no longer a depth) and more importantly universal (assuming you enforce comparability using interfaces) since objects define how they are sorted, so you can recycle the code infinitely. Much better =)
$result = array_filter($arr, function ($var) {
$found = false;
array_walk_recursive($var, function ($item, $key) use (&$found) {
$found = $found || $key == "name" && $item == "cat 1";
});
return $found;
});
http://snipplr.com/view/51108/nested-array-search-by-value-or-key/
<?php
//PHP 5.3
function searchNestedArray(array $array, $search, $mode = 'value') {
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key => $value) {
if ($search === ${${"mode"}})
return true;
}
return false;
}
$data = array(
array('abc', 'ddd'),
'ccc',
'bbb',
array('aaa', array('yyy', 'mp' => 555))
);
var_dump(searchNestedArray($data, 555));
function in_multi_array($needle, $key, $haystack)
{
$in_multi_array = false;
if (in_array($needle, $haystack))
{
$in_multi_array = true;
}else
{
foreach( $haystack as $key1 => $val )
{
if(is_array($val))
{
if($this->in_multi_array($needle, $key, $val))
{
$in_multi_array = true;
break;
}
}
}
}
return $in_multi_array;
}
I needed something similar, but to search for multidimensional array by value... I took John example and wrote
function _search_array_by_value($array, $value) {
$results = array();
if (is_array($array)) {
$found = array_search($value,$array);
if ($found) {
$results[] = $found;
}
foreach ($array as $subarray)
$results = array_merge($results, $this->_search_array_by_value($subarray, $value));
}
return $results;
}
I hope it helps somebody :)
This is a revised function from the one that John K. posted... I need to grab only the specific key in the array and nothing above it.
function search_array ( $array, $key, $value )
{
$results = array();
if ( is_array($array) )
{
if ( $array[$key] == $value )
{
$results[] = $array;
} else {
foreach ($array as $subarray)
$results = array_merge( $results, $this->search_array($subarray, $key, $value) );
}
}
return $results;
}
$arr = array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>2,name=>"cat 2"),
2 => array(id=>3,name=>"cat 1"));
print_r(search_array($arr, 'name', 'cat 1'));
function findKey($tab, $key){
foreach($tab as $k => $value){
if($k==$key) return $value;
if(is_array($value)){
$find = findKey($value, $key);
if($find) return $find;
}
}
return null;
}
I think the easiest way is using php array functions if you know your key.
function search_array ( $array, $key, $value )
{
return array_search($value,array_column($array,$key));
}
this return an index that you could find your desired data by this like below:
$arr = array(0 => array('id' => 1, 'name' => "cat 1"),
1 => array('id' => 2, 'name' => "cat 2"),
2 => array('id' => 3, 'name' => "cat 1")
);
echo json_encode($arr[search_array($arr,'name','cat 2')]);
this output will:
{"id":2,"name":"cat 2"}
And another version that returns the key value from the array element in which the value is found (no recursion, optimized for speed):
// if the array is
$arr['apples'] = array('id' => 1);
$arr['oranges'] = array('id' => 2);
//then
print_r(search_array($arr, 'id', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) )
// instead of Array ( [0] => Array ( [id] => 2 ) )
// search array for specific key = value
function search_array($array, $key, $value) {
$return = array();
foreach ($array as $k=>$subarray){
if (isset($subarray[$key]) && $subarray[$key] == $value) {
$return[$k] = $subarray;
return $return;
}
}
}
Thanks to all who posted here.
If you want to search for array of keys this is good
function searchKeysInMultiDimensionalArray($array, $keys)
{
$results = array();
if (is_array($array)) {
$resultArray = array_intersect_key($array, array_flip($keys));
if (!empty($resultArray)) {
$results[] = $resultArray;
}
foreach ($array as $subarray) {
$results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
}
}
return $results;
}
Keys will not overwrite because each set of key => values will be in separate array in resulting array.
If you don't want duplicate keys then use this one
function searchKeysInMultiDimensionalArray($array, $keys)
{
$results = array();
if (is_array($array)) {
$resultArray = array_intersect_key($array, array_flip($keys));
if (!empty($resultArray)) {
foreach($resultArray as $key => $single) {
$results[$key] = $single;
}
}
foreach ($array as $subarray) {
$results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
}
}
return $results;
}
2 functions: array_search_key_value which returns the array of keys to reach a key with a value in a multidimensional array, array_extract_keys which returns the value in a multidimensional array pointed to by an array of keys.
function array_search_key_value($array, $key, $value) {
if (!is_array($array)) {
return false;
}
return array_search_key_value_aux($array, $key, $value);
}
function array_search_key_value_aux($array, $key, $value, $path=null) {
if (array_key_exists($key, $array) && $array[$key] === $value) {
$path[]=$key;
return $path;
}
foreach ($array as $k => $v ) {
if (is_array($v)) {
$path[]=$k;
$p = array_search_key_value_aux($v, $key, $value, $path);
if ($p !== false) {
return $p;
}
}
}
return false;
}
function array_extract_keys($array, $key_list) {
$v = $array;
foreach ($key_list as $key) {
if (!is_array($v) || !array_key_exists($key, $v))
return false;
$v = &$v[$key];
}
return $v;
}
Here is a unitary test:
$test_array = array(
'a' => array(
'aa' => true,
'ab' => array(
'aaa' => array(
'one' => 1,
'two' => 2,
'three' => 3,
'four' => 4
),
'four' => 4,
'five' => 5,
),
'six' => 6,
),
'seven' => 7
);
$test_data = array(
array('one', 1),
array('two', 2),
array('three', 3),
array('four', 4),
array('five', 5),
array('six', 6),
array('seven', 7),
array('zero', 0),
array('one', 0),
);
foreach ($test_data as $d) {
$r = array_search_key_value($test_array, $d[0], $d[1]);
echo $d[0] . ' => ' . $d[1] . ' ? ', $r ? implode('/', $r) . ' => ' . array_extract_keys($test_array, $r) : 'null', PHP_EOL;
}