Recursively remove empty elements and subarrays from a multi-dimensional array - php

I can't seem to find a simple, straight-forward solution to the age-old problem of removing empty elements from arrays in PHP.
My input array may look like this:
Array ( [0] => Array ( [Name] => [EmailAddress] => ) )
(And so on, if there's more data, although there may not be...)
If it looks like the above, I want it to be completely empty after I've processed it.
So print_r($array); would output:
Array ( )
If I run $arrayX = array_filter($arrayX); I still get the same print_r output. Everywhere I've looked suggests this is the simplest way of removing empty array elements in PHP5, however.
I also tried $arrayX = array_filter($arrayX,'empty_array'); but I got the following error:
Warning: array_filter() [function.array-filter]: The second argument, 'empty_array', should be a valid callback
What am I doing wrong?

Try using array_map() to apply the filter to every array in $array:
$array = array_map('array_filter', $array);
$array = array_filter($array);
Demo: http://codepad.org/xfXEeApj

There are numerous examples of how to do this. You can try the docs, for one (see the first comment).
function array_filter_recursive($array, $callback = null) {
foreach ($array as $key => & $value) {
if (is_array($value)) {
$value = array_filter_recursive($value, $callback);
}
else {
if ( ! is_null($callback)) {
if ( ! $callback($value)) {
unset($array[$key]);
}
}
else {
if ( ! (bool) $value) {
unset($array[$key]);
}
}
}
}
unset($value);
return $array;
}
Granted this example doesn't actually use array_filter but you get the point.

The accepted answer does not do exactly what the OP asked. If you want to recursively remove ALL values that evaluate to false including empty arrays then use the following function:
function array_trim($input) {
return is_array($input) ? array_filter($input,
function (& $value) { return $value = array_trim($value); }
) : $input;
}
Or you could change the return condition according to your needs, for example:
{ return !is_array($value) or $value = array_trim($value); }
If you only want to remove empty arrays. Or you can change the condition to only test for "" or false or null, etc...

Following up jeremyharris' suggestion, this is how I needed to change it to make it work:
function array_filter_recursive($array) {
foreach ($array as $key => &$value) {
if (empty($value)) {
unset($array[$key]);
}
else {
if (is_array($value)) {
$value = array_filter_recursive($value);
if (empty($value)) {
unset($array[$key]);
}
}
}
}
return $array;
}

Try with:
$array = array_filter(array_map('array_filter', $array));
Example:
$array[0] = array(
'Name'=>'',
'EmailAddress'=>'',
);
print_r($array);
$array = array_filter(array_map('array_filter', $array));
print_r($array);
Output:
Array
(
[0] => Array
(
[Name] =>
[EmailAddress] =>
)
)
Array
(
)

array_filter() is not type-sensitive by default. This means that any zero-ish, false-y, null, empty values will be removed. My links to follow will demonstrate this point.
The OP's sample input array is 2-dimensional. If the data structure is static then recursion is not necessary. For anyone who would like to filter the zero-length values from a multi-dimensional array, I'll provide a static 2-dim method and a recursive method.
Static 2-dim Array:
This code performs a "zero-safe" filter on the 2nd level elements and then removes empty subarrays: (See this demo to see this method work with different (trickier) array data)
$array=[
['Name'=>'','EmailAddress'=>'']
];
var_export(
array_filter( // remove the 2nd level in the event that all subarray elements are removed
array_map( // access/iterate 2nd level values
function($v){
return array_filter($v,'strlen'); // filter out subarray elements with zero-length values
},$array // the input array
)
)
);
Here is the same code as a one-liner:
var_export(array_filter(array_map(function($v){return array_filter($v,'strlen');},$array)));
Output (as originally specified by the OP):
array (
)
*if you don't want to remove the empty subarrays, simply remove the outer array_filter() call.
Recursive method for multi-dimensional arrays of unknown depth: When the number of levels in an array are unknown, recursion is a logical technique. The following code will process each subarray, removing zero-length values and any empty subarrays as it goes. Here is a demo of this code with a few sample inputs.
$array=[
['Name'=>'','Array'=>['Keep'=>'Keep','Drop'=>['Drop2'=>'']],'EmailAddress'=>'','Pets'=>0,'Children'=>null],
['Name'=>'','EmailAddress'=>'','FavoriteNumber'=>'0']
];
function removeEmptyValuesAndSubarrays($array){
foreach($array as $k=>&$v){
if(is_array($v)){
$v=removeEmptyValuesAndSubarrays($v); // filter subarray and update array
if(!sizeof($v)){ // check array count
unset($array[$k]);
}
}elseif(!strlen($v)){ // this will handle (int) type values correctly
unset($array[$k]);
}
}
return $array;
}
var_export(removeEmptyValuesAndSubarrays($array));
Output:
array (
0 =>
array (
'Array' =>
array (
'Keep' => 'Keep',
),
'Pets' => 0,
),
1 =>
array (
'FavoriteNumber' => '0',
),
)
If anyone discovers an input array that breaks my recursive method, please post it (in its simplest form) as a comment and I'll update my answer.

When this question was asked, the latest version of PHP was 5.3.10. As of today, it is now 8.1.1, and a lot has changed since! Some of the earlier answers will provide unexpected results, due to changes in the core functionality. Therefore, I feel an up-to-date answer is required. The below will iterate through an array, remove any elements that are either an empty string, empty array, or null (so false and 0 will remain), and if this results in any more empty arrays, it will remove them too.
function removeEmptyArrayElements( $value ) {
if( is_array($value) ) {
$value = array_map('removeEmptyArrayElements', $value);
$value = array_filter( $value, function($v) {
// Change the below to determine which values get removed
return !( $v === "" || $v === null || (is_array($v) && empty($v)) );
} );
}
return $value;
}
To use it, you simply call removeEmptyArrayElements( $array );

If used inside class as helper method:
private function arrayFilterRecursive(array $array): array
{
foreach ($array as $key => &$value) {
if (empty($value)) {
unset($array[$key]);
} else if (is_array($value)) {
$value = self::arrayFilterRecursive($value);
}
}
return $array;
}

Related

How can you count the number keys in an array that have a non-empty corresponding value? [duplicate]

I can't seem to find a simple, straight-forward solution to the age-old problem of removing empty elements from arrays in PHP.
My input array may look like this:
Array ( [0] => Array ( [Name] => [EmailAddress] => ) )
(And so on, if there's more data, although there may not be...)
If it looks like the above, I want it to be completely empty after I've processed it.
So print_r($array); would output:
Array ( )
If I run $arrayX = array_filter($arrayX); I still get the same print_r output. Everywhere I've looked suggests this is the simplest way of removing empty array elements in PHP5, however.
I also tried $arrayX = array_filter($arrayX,'empty_array'); but I got the following error:
Warning: array_filter() [function.array-filter]: The second argument, 'empty_array', should be a valid callback
What am I doing wrong?
Try using array_map() to apply the filter to every array in $array:
$array = array_map('array_filter', $array);
$array = array_filter($array);
Demo: http://codepad.org/xfXEeApj
There are numerous examples of how to do this. You can try the docs, for one (see the first comment).
function array_filter_recursive($array, $callback = null) {
foreach ($array as $key => & $value) {
if (is_array($value)) {
$value = array_filter_recursive($value, $callback);
}
else {
if ( ! is_null($callback)) {
if ( ! $callback($value)) {
unset($array[$key]);
}
}
else {
if ( ! (bool) $value) {
unset($array[$key]);
}
}
}
}
unset($value);
return $array;
}
Granted this example doesn't actually use array_filter but you get the point.
The accepted answer does not do exactly what the OP asked. If you want to recursively remove ALL values that evaluate to false including empty arrays then use the following function:
function array_trim($input) {
return is_array($input) ? array_filter($input,
function (& $value) { return $value = array_trim($value); }
) : $input;
}
Or you could change the return condition according to your needs, for example:
{ return !is_array($value) or $value = array_trim($value); }
If you only want to remove empty arrays. Or you can change the condition to only test for "" or false or null, etc...
Following up jeremyharris' suggestion, this is how I needed to change it to make it work:
function array_filter_recursive($array) {
foreach ($array as $key => &$value) {
if (empty($value)) {
unset($array[$key]);
}
else {
if (is_array($value)) {
$value = array_filter_recursive($value);
if (empty($value)) {
unset($array[$key]);
}
}
}
}
return $array;
}
Try with:
$array = array_filter(array_map('array_filter', $array));
Example:
$array[0] = array(
'Name'=>'',
'EmailAddress'=>'',
);
print_r($array);
$array = array_filter(array_map('array_filter', $array));
print_r($array);
Output:
Array
(
[0] => Array
(
[Name] =>
[EmailAddress] =>
)
)
Array
(
)
array_filter() is not type-sensitive by default. This means that any zero-ish, false-y, null, empty values will be removed. My links to follow will demonstrate this point.
The OP's sample input array is 2-dimensional. If the data structure is static then recursion is not necessary. For anyone who would like to filter the zero-length values from a multi-dimensional array, I'll provide a static 2-dim method and a recursive method.
Static 2-dim Array:
This code performs a "zero-safe" filter on the 2nd level elements and then removes empty subarrays: (See this demo to see this method work with different (trickier) array data)
$array=[
['Name'=>'','EmailAddress'=>'']
];
var_export(
array_filter( // remove the 2nd level in the event that all subarray elements are removed
array_map( // access/iterate 2nd level values
function($v){
return array_filter($v,'strlen'); // filter out subarray elements with zero-length values
},$array // the input array
)
)
);
Here is the same code as a one-liner:
var_export(array_filter(array_map(function($v){return array_filter($v,'strlen');},$array)));
Output (as originally specified by the OP):
array (
)
*if you don't want to remove the empty subarrays, simply remove the outer array_filter() call.
Recursive method for multi-dimensional arrays of unknown depth: When the number of levels in an array are unknown, recursion is a logical technique. The following code will process each subarray, removing zero-length values and any empty subarrays as it goes. Here is a demo of this code with a few sample inputs.
$array=[
['Name'=>'','Array'=>['Keep'=>'Keep','Drop'=>['Drop2'=>'']],'EmailAddress'=>'','Pets'=>0,'Children'=>null],
['Name'=>'','EmailAddress'=>'','FavoriteNumber'=>'0']
];
function removeEmptyValuesAndSubarrays($array){
foreach($array as $k=>&$v){
if(is_array($v)){
$v=removeEmptyValuesAndSubarrays($v); // filter subarray and update array
if(!sizeof($v)){ // check array count
unset($array[$k]);
}
}elseif(!strlen($v)){ // this will handle (int) type values correctly
unset($array[$k]);
}
}
return $array;
}
var_export(removeEmptyValuesAndSubarrays($array));
Output:
array (
0 =>
array (
'Array' =>
array (
'Keep' => 'Keep',
),
'Pets' => 0,
),
1 =>
array (
'FavoriteNumber' => '0',
),
)
If anyone discovers an input array that breaks my recursive method, please post it (in its simplest form) as a comment and I'll update my answer.
When this question was asked, the latest version of PHP was 5.3.10. As of today, it is now 8.1.1, and a lot has changed since! Some of the earlier answers will provide unexpected results, due to changes in the core functionality. Therefore, I feel an up-to-date answer is required. The below will iterate through an array, remove any elements that are either an empty string, empty array, or null (so false and 0 will remain), and if this results in any more empty arrays, it will remove them too.
function removeEmptyArrayElements( $value ) {
if( is_array($value) ) {
$value = array_map('removeEmptyArrayElements', $value);
$value = array_filter( $value, function($v) {
// Change the below to determine which values get removed
return !( $v === "" || $v === null || (is_array($v) && empty($v)) );
} );
}
return $value;
}
To use it, you simply call removeEmptyArrayElements( $array );
If used inside class as helper method:
private function arrayFilterRecursive(array $array): array
{
foreach ($array as $key => &$value) {
if (empty($value)) {
unset($array[$key]);
} else if (is_array($value)) {
$value = self::arrayFilterRecursive($value);
}
}
return $array;
}

Push something into a specified position in an array in php

if I give you an array:
$a = array('something' => 'value', 'apple' => 'sauce');
and said given the key, find the position and insert something before it - what would you do?
I can find something by doing:
function insert($value, $array)
foreach($array as $k=>$v) {
if ($k === $value) {
// I am stuck here ...
}
}
}
But I don't know how to insert before $k in the array. This example assumes the array always has key=>value.
Update 1
Apologies all, I don't think I was very clear. Given the above information the goal is to use this "insert" function to insert a new key=>value, so given the above sample array, I want to do:
insert(array('new_key' => 'new_value'), 'something', $a)
So the above for loop would have to be changed to:
function insert($array, $key, $originalArray)
foreach($originalArray as $k=>$v) {
if ($k === $key) {
// Insert $array right before 'something'
// I am stuck here ...
}
}
}
The result would be a new array that looks like:
$a = array('new_key' => 'new_value', 'something' => 'value', 'apple' => 'sauce');
The goal is to find the key in the given array and insert the new array right before it. Both arrays must be key/value.
Here is a function which will insert an element into an array before or after the chosen value. It returns the modified array.
function associativeArrayInject($originalArray, $targetKey, $newKey, $newValue, $insertBefore=true){
# We will build a new array from the ground up, and return it. This is that array.
$newArray = array();
# Loop over the original array.
foreach($originalArray as $key => $value){
# If we need to inject the data before the current key, we
# do that here
if($key === $targetKey && $insertBefore)
$newArray[$newKey] = $newValue;
# At this point, we insert the $originalArray's key and value
# because if we needed to inject before, it's already been done,
# and if we need to inject after, we'll do that next
$newArray[$key] = $value;
# If we need to inject the data after the current key, we
# do that here
if($key === $targetKey && !$insertBefore)
$newArray[$newKey] = $newValue;
}
# When all the array values are looped over, the new array will have
# been constructed with the new data in the appropriate spot. Now
# we can return it.
return $newArray;
}
Sol. 1: Try to use arsort() or similar functions of sorting arrays in php.
Sol. 2: At first init array of values and than use foreach loop to sort array how you like.
Sol. 3: You can create multidimensional array, something like this:
$array[$position]['something']['value'];
And than use ksort function to sort array by keys. So, this is my idea.
Also, show exactly how you wana sort array. ( like input ( some data ) => output ( some data ) )
Several variations whether you want to modify in place with a reference or return the new array:
function insert(&$array, $search, $value) {
$i = 0;
foreach($array as $k => $v) {
if ($k === $search) {
return array_splice($array, $i+1, 0, $value);
}
$i++;
}
}
$a = array('foo' => 'bar', 'something' => 'value', 'apple' => 'sauce');
insert($a, 'something', 'new value');

PHP value in an array

Hi I'm working on a checking a given array for a certain value.
my array looks like
while($row = mysqli_fetch_array($result)){
$positions[] = array( 'pos' => $row['pos'], 'mark' => $row['mark'] );
}
I'm trying to get the info from it with a method like
<?php
if(in_array('1', $positions)){
echo "x";
}
?>
This I know the value '1' is in the array but the x isn't being sent as out put. any suggestions on how to get "1" to be recognized as being in the array?
Edit:
I realize that this is an array inside of an array. is it possible to combine in_array() to say something like:
"is the value '1' inside one of these arrays"
in_array is not recursive. You're checking if 1 is in an array of arrays which doesn't make sense. You'll have to loop over each element and check that way.
$in = false;
foreach ($positions as $pos) {
if (in_array(1, $pos)) {
$in = true;
break;
}
}
in_array only checks the first level. In this case, it only sees a bunch of arrays, no numbers of any kind. Instead, consider looping through the array with foreach, and check if that 1 is where you expect it to be.
That's because $positions is an array of arrays (a multi-dimensional array).
It contains no simple '1'.
Try a foreach-loop instead:
foreach($postions as $value)
if ($value["pos"] == '1')
echo "x ".$value["mark"];
The problem is that 1 isn't actually in the array. It's in one of the array's within the array. You are comparing the value 1 to the value Array which obviously isn't the same.
Something like this should get you started:
foreach ($positions as $position) {
if ($position['pos'] == 1) {
echo "x";
break;
}
}
Your array $positions is recursive, due to the fact that you use $positions[] in your first snippet. in_array is not recursive (see the PHP manual). Therefore, you need a custom function which works with recursive arrays (source):
<?php
function in_arrayr( $needle, $haystack ) {
foreach( $haystack as $v ){
if( $needle == $v )
return true;
elseif( is_array( $v ) )
if( in_arrayr( $needle, $v ) )
return true;
}
return false;
}
?>
Instead of calling in_array() in your script, you should now call in_arrayr().

How to remove all instances of duplicated values from an array

I know there is array_unique function, but I want to remove duplicates. Is there a built-in function or do I have to roll my own.
Example input:
banna, banna, mango, mango, apple
Expected output:
apple
You can use a combination of array_unique, array_diff_assoc and array_diff:
array_diff($arr, array_diff_assoc($arr, array_unique($arr)))
You can use
$singleOccurences = array_keys(
array_filter(
array_count_values(
array('banana', 'mango', 'banana', 'mango', 'apple' )
),
function($val) {
return $val === 1;
}
)
)
See
array_count_values — Counts all the values of an array
array_filter — Filters elements of an array using a callback function
array_keys — Return all the keys or a subset of the keys of an array
callbacks
Just write your own simple foreach loop:
$used = array();
$array = array("banna","banna","mango","mango","apple");
foreach($array as $arrayKey => $arrayValue){
if(isset($used[$arrayValue])){
unset($array[$used[$arrayValue]]);
unset($array[$arrayKey]);
}
$used[$arrayValue] = $arrayKey;
}
var_dump($array); // array(1) { [4]=> string(5) "apple" }
have fun :)
If you want to only leave values in the array that are already unique, rather than select one unique instance of each value, you will indeed have to roll your own. Built in functionality is just there to sanitise value sets, rather than filter.
You want to remove any entries that have duplicates, so that you're left with only the entries that were unique in the list?
Hmm it does sound like something you'll need to roll your own.
There is no existing function; You'll have to do this in two passes, one to count the unique values and one to extract the unique values:
$count = array();
foreach ($values as $value) {
if (array_key_exists($value, $count))
++$count[$value];
else
$count[$value] = 1;
}
$unique = array();
foreach ($count as $value => $count) {
if ($count == 1)
$unique[] = $value;
}
The answer on top looks great, but on a side note: if you ever want to eliminate duplicates but leave the first one, using array_flip twice would be a pretty simple way to do so. array_flip(array_flip(x))
Only partially relevant to this specific question - but I created this function from Gumbo's answer for multi dimensional arrays:
function get_default($array)
{
$default = array_column($array, 'default', 'id');
$array = array_diff($default, array_diff_assoc($default, array_unique($default)));
return key($array);
}
In this example, I had cached statuses and each one other than the default was 0 (the default was 1). I index the default array from the IDs, and then turn it into a string. So to be clear - the returned result of this is the ID of the default status providing it's in the same part of the multi dimensional array and not the key of it
PHP.net http://php.net/manual/en/function.array-unique.php
array array_unique ( array $array [, int $sort_flags = SORT_STRING ] )
Takes an input array and returns a new array without duplicate values.
New solution:
function remove_dupes(array $array){
$ret_array = array();
foreach($array as $key => $val){
if(count(array_keys($val) > 1){
continue;
} else {
$ret_array[$key] = $val;
}
}

selecting an array key based on partial string

I have an array and in that array I have an array key that looks like, show_me_160 this array key may change a little, so sometimes the page may load and the array key maybe show_me_120, I want to now is possible to just string match the array key up until the last _ so that I can check what the value is after the last underscore?
one solution i can think of:
foreach($myarray as $key=>$value){
if("show_me_" == substr($key,0,8)){
$number = substr($key,strrpos($key,'_'));
// do whatever you need to with $number...
}
}
I ran into a similar problem recently. This is what I came up with:
$value = $my_array[current(preg_grep('/^show_me_/', array_keys($my_array)))];
you would have to iterate over your array to check each key separately, since you don't have the possibility to query the array directly (I'm assuming the array also holds totally unrelated keys, but you can skip the if part if that's not the case):
foreach($array as $k => $v)
{
if (strpos($k, 'show_me_') !== false)
{
$number = substr($k, strrpos($k, '_'));
}
}
However, this sounds like a very strange way of storing data, and if I were you, I'd check if there's not an other way (more efficient) of passing data around in your application ;)
to search for certain string in array keys you can use array_filter(); see docs
// the array you'll search in
$array = ["search_1"=>"value1","search_2"=>"value2","not_search"=>"value3"];
// filter the array and assign the returned array to variable
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($key){
return(strpos($key,'search_') !== false);
},
// flag to let the array_filter(); know that you deal with array keys
ARRAY_FILTER_USE_KEY
);
// print out the returned array
print_r($foo);
if you search in the array values you can use the flag 0 or leave the flag empty
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($value){
return(strpos($value,'value') !== false);
},
// flag to let the array_filter(); know that you deal with array value
0
);
or
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($value){
return(strpos($value,'value') !== false);
}
);
if you search in the array values and array keys you can use the flag ARRAY_FILTER_USE_BOTH
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($value, $key){
return(strpos($key,'search_') !== false or strpos($value,'value') !== false);
},
ARRAY_FILTER_USE_BOTH
);
in case you'll search for both you have to pass 2 arguments to the callback function
You can also use a preg_match based solution:
foreach($array as $str) {
if(preg_match('/^show_me_(\d+)$/',$str,$m)) {
echo "Array element ",$str," matched and number = ",$m[1],"\n";
}
}
filter_array($array,function ($var){return(strpos($var,'searched_word')!==FALSE);},);
return array 'searched_key' => 'value assigned to the key'
foreach($myarray as $key=>$value)
if(count(explode('show_me_',$event_key)) > 1){
//if array key contains show_me_
}
More information (example):
if array key contain 'show_me_'
$example = explode('show_me_','show_me_120');
print_r($example)
Array ( [0] => [1] => 120 )
print_r(count($example))
2
print_r($example[1])
120

Categories