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;
}
Related
I have a deep multidimensional array that I am needing to extract the value of a specific key. I have found that the array_walk_recursive function will be my best option. I only need the first occurrence.
My array looks like this - (except much more complicated)
Array (
[vehicle info] => Array (
[one] => Array (
[submodel] => LX
[engine] => 2.3
)
[two] => Array (
[color] => blue
[year] => 2007
[wheels] => 4
)
[three] => Array (
[submodel] => LX
[make] => Ford
[model] => F-150
[offroad] => No
)
)
)
The issue here is, submodel is in both one and three. Additionally, the array is not consistent, so I must use array_walk_recursive to search through it for the matching key, then return the value for that key.
Here is my current code -
array_walk_recursive ($array, (function ($item, $key) {
$wanted = "submodel";
if ($key === $wanted) {
echo ("$key is $item");
}
}));
The above returns submodel is LXsubmodel is LX.
Bonus Question!!
How can I search for multiple keys and return the first corresponding value for each of those? I was thinking putting all wanted keys in an array, then do a foreach loop, but don't quite know how to structure this. I am new to php.
array_walk_recursive() is the appropriate native function to call for this task. Keep track of which keys have already been declared in the result array and ensure that they are never overwritten.
Code: (Demo)
$needles = ['submodel', 'offroad'];
$result = [];
array_walk_recursive(
$array,
function($value, $key) use ($needles, &$result) {
if (
in_array($key, $needles)
&& !isset($result[$key])
) {
$result[$key] = "$key is $value";
}
}
);
var_export($result);
Output:
array (
'submodel' => 'submodel is LX',
'offroad' => 'offroad is No',
)
If your application has performance concerns, then the native function becomes less attractive because it will always iterate the entire input array's structure -- even after all sought keys are encountered. If you want to "break early" (short circuit), then you will need to design your own recursive function which will return when all sought keys are found.
Code: (Demo)
$soughtKeys = array_flip(['submodel', 'offroad']);
function earlyReturningRecursion(array $array, array $soughtKeys, array &$result = []): array
{
foreach ($array as $key => $value) {
if (!array_diff_key($soughtKeys, $result)) { // check if result is complete
return $result;
} elseif (is_array($value)) {
earlyReturningRecursion($value, $soughtKeys, $result);
} elseif (isset($soughtKeys[$key]) && !isset($result[$key])) {
$result[$key] = "$key is $value";
}
}
return $result;
}
var_export(earlyReturningRecursion($array, $soughtKeys));
// same output as the first snippet
I would start by setting the values you want to null, and then only saving them if they haven't been found yet, by checking is_null(). I haven't tested this code, but it should look something like this:
$submodel = null;
array_walk_recursive ($array, (function ($item, $key) {
$wanted = "submodel";
if ($key === $wanted && is_null($submodel)) {
echo ("$key is $item");
$submodel = $item;
}
}));
array_walk_recursive() has the defect of not allowing to return matching results however in PHP 7 you could use an anonymous function and a variable to store the matching value.
$matching = null;
$wanted = "submodel";
array_walk_recursive ($array, function ($item, $key) use ($wanted, $matching) {
if (($key === $wanted) && is_null($matching)) {
$matching = $item;
}
});
As far as there is no way to return early from array_walk_recursive(), I'd suggest to create a function to find the first occurrence of $wanted:
$arr = [
'vehicle info' => [
'one' => ['submodel' => 'LX', 'engine' => '2.3'],
'two' => ['color' => 'blue', 'year' => '2007', 'wheels' => '4'],
'three' => ['submodel' => 'LX', 'make' => 'Ford', 'model' => 'F-150', 'offroad' => 'No'],
],
];
function find($needle, $haystack, $found = '')
{
foreach ($haystack as $key => $value) {
if ($found) {
break;
}
if ($key === $needle) {
$found = "{$needle} is {$value}";
break;
}
if (is_array($value)) {
$found = find($needle, $value, $found);
}
}
return $found;
}
$wanted = 'submodel';
$result = find($wanted, $arr);
var_dump($result); // string(14) "submodel is LX"
Live demo
Update: to search for multiple keys you'll need to do it in a loop:
$multiple_keys = array('submodel', 'year');
foreach ($multiple_keys as $wanted) {
var_dump(find($wanted, $arr));
}
// Output:
// string(14) "submodel is LX"
// string(12) "year is 2007"
Live demo
This question already has answers here:
Group 2d array rows by one column and sum another column [duplicate]
(3 answers)
Closed 4 months ago.
I have a multi array that has some duplicated values that are same by name ( name is an element )
i want to sum quantity of each array that has same name , and then unset the second array
Example :
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>500),
1=>array("name"=>"y","QTY"=>250),
2=>array("name"=>"X","QTY"=>250)
);
?>
Now i want to sum duplicated values as below.
Result :
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>750),
1=>array("name"=>"y","QTY"=>250)
);
?>
UPDATED
i found this function to search in array , foreach and another loops does not works too
<?php
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;
}
?>
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>500),
1=>array("name"=>"y","QTY"=>250),
2=>array("name"=>"X","QTY"=>250)
);
$result = array();
$names = array_column($Array, 'name');
$QTYs = array_column($Array, 'QTY');
$unique_names = array_unique($names);
foreach ($unique_names as $name){
$this_keys = array_keys($names, $name);
$qty = array_sum(array_intersect_key($QTYs, array_combine($this_keys, $this_keys)));
$result[] = array("name"=>$name,"QTY"=>$qty);
}
var_export($result); :
array (
0 =>
array (
'name' => 'X',
'QTY' => 750,
),
1 =>
array (
'name' => 'y',
'QTY' => 250,
),
)
Try this simplest one, Hope this will be helpful.
Try this code snippet here
$result=array();
foreach ($Array as $value)
{
if(isset($result[$value["name"]]))
{
$result[$value["name"]]["QTY"]+=$value["QTY"];
}
else
{
$result[$value["name"]]=$value;
}
}
print_r(array_values($result));
Try this, check the live demo.
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>500),
1=>array("name"=>"y","QTY"=>250),
2=>array("name"=>"X","QTY"=>250)
);
$keys = array_column($Array, 'name');
$QTYs = array_column($Array, 'QTY');
$result = [];
foreach($keys as $k => $v)
{
$result[$v] += $QTYs[$k];
}
print_r($result);
You can achieve this by creating an array with name as key and then iterating over all values and add them together, resulting in this
function sum_same($array) {
$keyArray = [];
foreach ($array as $entry) {
$name = $entry["name"];
if(isset($keyArray[$name])) {
$keyArray[$name] += $entry["QTY"];
} else {
$keyArray[$name] = $entry["QTY"];
}
}
// Convert the keyArray to the old format.
$resultArray = [];
foreach ($keyArray as $key => $value) {
$resultArray[] = ["name" => $key, "QTY" => $value];
}
return $resultArray;
}
Try the code here
If you want to alter the old array use the function like this:
$myArray = sum_same($myArray);
The old array will be overwritten by the new one.
This problem is a classic example of usage for array_reduce():
$Array = array(
0 => array('name' => 'X', 'QTY' => 500),
1 => array('name' => 'y', 'QTY' => 250),
2 => array('name' => 'X', 'QTY' => 250),
);
// array_values() gets rid of the keys of the array produced by array_reduce()
// they were needed by the callback to easily identify the items in the array during processing
$Array = array_values(array_reduce(
$Array,
function (array $a, array $v) {
$k = $v['name'];
// Check if another entry having the same name was already processed
// Keep them in the accumulator indexed by name
if (! array_key_exists($k, $a)) {
$a[$k] = $v; // This is the first entry with this name
} else {
// Not the first one; update the quantity
$a[$k]['QTY'] += $v['QTY'];
}
return $a; // return the partial accumulator
},
array() // start with an empty array as accumulator
));
I tried to get array of keys of searched element`s key in multidimensional array. For example, my initial array is:
$f['Kitchen']['Dishes']['Mantovarka']=3;
$f['Kitchen']['Dishes']['Castrool']=91;
$f['Kitchen']['Dishes']['Separator']=10;
$f['Kitchen']['Product']=18;
$f['Kitchen']['Textile']=19;
$f['Kitchen']['Blue things One']['Juicemaker']=25;
$f['Kitchen']['Blue things One']['Freegener']=13;
$f['Kitchen']['Blue things']['Microwave']=4;
$f['Kitchen']['Blue things']['Iron']=24;
If I try to get array of keys for 'Separator' key with this function:
$index=0; $array =[];
function getArrayOfkeys($needle, $haystack,$original,$array,$index) {
$index++;
$exists = false;
if(is_array($haystack)){
foreach ($haystack as $key => $val) {
$array[$index]=$key;
if($key == $needle){
$exists = true;
break;
}elseif(is_array($val)){
return getArrayOfkeys($needle, $val,$original,$array,$index);
}
}
}else{
$index--;
}
if($exists==true){
return $array;
}
else{
// I need here logic!!!
}
}
it returns me:
[
1 => 'Kitchen'
2 => 'Dishes'
3 => 'Separator'
]
it is fine!
But when I try to get array of keys of 'Juicemaker' or 'Iron' or 'Product'. It is not working, because I can`t call my getArrayOfkeys() function after element:
$f['Kitchen']['Dishes']['Separator']=10;
How can I call again after abovementioned element?
I want to get for 'Juicemaker' key ['Kitchen', 'Blue things One', 'Juicemaker'].
For 'Product' key ['Kitchen','Product'].
For 'Iron' key ['Kitchen', 'Blue things','Iron']
My function seems to me very unoptimized, help me, please, with optimize it.
Please try this function:
function findKeyPath($arr, $key, $path = '') {
foreach($arr as $k => $value) {
if(is_array($arr[$k])) {
$ret = findKeyPath($arr[$k], $key, $path.$k.',');
if(is_array($ret)) {
return $ret;
}
}else if($k == $key) {
return explode(',', $path.$key);
}
}
return null;
}
print_r(findKeyPath($f, 'Juicemaker'));
//Output: Array ( [0] => Kitchen [1] => Blue things One2 [2] => Juicemaker )
Demo: https://3v4l.org/YJD7q
I hope this will help you.
Given the following array $mm
Array
(
[147] => Array
(
[pts_m] =>
[pts_mreg] => 1
[pts_cg] => 1
)
[158] => Array
(
[pts_m] =>
[pts_mreg] =>
[pts_cg] => 0
)
[159] => Array
(
[pts_m] =>
[pts_mreg] => 1
[pts_cg] => 1
)
)
When I run count(array_filter($mm)) I get 3 as result since it is not recursive.
count(array_filter($mm), COUNT_RECURSIVE) also will not do because I actually need to run the array_filter recursively, and then count its result.
So my question is: how do I recursively run array_filter($mm) in this case?
My expected result here would be 4.
Please note that I am not using any callback so I can exclude false, null and empty.
From the PHP array_filter documentation:
//This function filters an array and remove all null values recursively.
<?php
function array_filter_recursive($input)
{
foreach ($input as &$value)
{
if (is_array($value))
{
$value = array_filter_recursive($value);
}
}
return array_filter($input);
}
?>
//Or with callback parameter (not tested) :
<?php
function array_filter_recursive($input, $callback = null)
{
foreach ($input as &$value)
{
if (is_array($value))
{
$value = array_filter_recursive($value, $callback);
}
}
return array_filter($input, $callback);
}
?>
Should work
$count = array_sum(array_map(function ($item) {
return ((int) !is_null($item['pts_m'])
+ ((int) !is_null($item['pts_mreg'])
+ ((int) !is_null($item['pts_cg']);
}, $array);
or maybe
$count = array_sum(array_map(function ($item) {
return array_sum(array_map('is_int', $item));
}, $array);
There are definitely many more possible solutions. If you want to use array_filter() (without callback) remember, that it treats 0 as false too and therefore it will remove any 0-value from the array.
If you are using PHP in a pre-5.3 version, I would use a foreach-loop
$count = 0;
foreach ($array as $item) {
$count += ((int) !is_null($item['pts_m'])
+ ((int) !is_null($item['pts_mreg'])
+ ((int) !is_null($item['pts_cg']);
}
Update
Regarding the comment below:
Thx #kc I actually want the method to remove false, 0, empty etc
When this is really only, what you want, the solution is very simple too.
But now I don't know, how to interpret
My expected result here would be 5.
Anyway, its short now :)
$result = array_map('array_filter', $array);
$count = array_map('count', $result);
$countSum = array_sum($count);
The resulting array looks like
Array
(
[147] => Array
(
[pts_mreg] => 1
[pts_cg] => 1
)
[158] => Array
(
)
[159] => Array
(
[pts_mreg] => 1
[pts_cg] => 1
)
)
A better alternative
One implementation that always worked for me is this one:
function filter_me(&$array) {
foreach ( $array as $key => $item ) {
is_array ( $item ) && $array [$key] = filter_me ( $item );
if (empty ( $array [$key] ))
unset ( $array [$key] );
}
return $array;
}
I notice that someone had created a similar function except that this one presents, in my opinion, few advantages:
you pass an array as reference (not its copy) and thus the algorithm is memory-friendly
no additional calls to array_filter which in reality involves:
the use of stack, ie. additional memory
some other operations, ie. CPU cycles
Benchmarks
A 64MB array
filter_me function finished in 0.8s AND the PHP allocated memory before starting the function was 65MB, when function returned it was 39.35MB !!!
array_filter_recursive function recommended above by Francois Deschenes had no chance; after 1s PHP Fatal error: Allowed memory size of 134217728 bytes exhausted
A 36MB array
filter_me function finished in 0.4s AND the PHP allocated memory before starting the function was 36.8MB, when function returned it was 15MB !!!
array_filter_recursive function succeeded this time in 0.6s and memory before/after was quite the same
I hope it helps.
This function effectively applies filter_recursive with a provided callback
class Arr {
public static function filter_recursive($array, $callback = NULL)
{
foreach ($array as $index => $value)
{
if (is_array($value))
{
$array[$index] = Arr::filter_recursive($value, $callback);
}
else
{
$array[$index] = call_user_func($callback, $value);
}
if ( ! $array[$index])
{
unset($array[$index]);
}
}
return $array;
}
}
And you'd use it this way:
Arr::filter_recursive($my_array, $my_callback);
This might help someone
I needed an array filter recursive function that would walk through all nodes (including arrays, so that we have the possibility to discard entire arrays), and so I came up with this:
public static function filterRecursive(array $array, callable $callback): array
{
foreach ($array as $k => $v) {
$res = call_user_func($callback, $v);
if (false === $res) {
unset($array[$k]);
} else {
if (is_array($v)) {
$array[$k] = self::filterRecursive($v, $callback);
}
}
}
return $array;
}
See more examples here: https://github.com/lingtalfi/Bat/blob/master/ArrayTool.md#filterrecursive
This should work for callback and mode support along with an optional support for depth.
function array_filter_recursive(array $array, callable $callback = null, int $mode = 0, int $depth = -1): array
{
foreach ($array as & $value) {
if ($depth != 0 && is_array($value)) {
$value = array_filter_recursive($value, $callback, $mode, $depth - 1);
}
}
if ($callback) {
return array_filter($array, $callback, $mode);
}
return array_filter($array);
}
Calling the function with $depth = 0 for nested arrays, will yield the same result as array_filter.
This strike me as an XY Problem.
Recursion is not necessary because the array has a consistent depth of 2 levels.
It is not necessary to generate an array of filtered elements so that you can traverse the filtered data to count it. Just traverse once and add 1 to the count variable whenever a truthy value is encountered.
The following snippet calls no functions (only language constructs -- foreach()) and therefore will be highly efficient.
Code: (Demo)
$truthyCount = 0;
foreach ($array as $row) {
foreach ($row as $v) {
$truthyCount += (bool) $v;
}
}
var_export($truthyCount);
<?php
$mm = array
(
147 => array
(
"pts_m" => "",
"pts_mreg" => 1,
"pts_cg" => 1
) ,
158 => array
(
"pts_m" => null ,
"pts_mreg" => null,
"pts_cg" => 0
),
159 => array
(
"pts_m" => "",
"pts_mreg" => 1,
"pts_cg" => 1
)
);
$count = 0;
foreach ($mm as $m) {
foreach ($m as $value) {
if($value !== false && $value !== "" && $value !== null) {
$count++;
}
}
}
echo $count;
?>
I have a nested array in which I want to display a subset of results. For example, on the array below I want to loop through all the values in nested array[1].
Array
(
[0] => Array
(
[0] => one
[1] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
)
[1] => Array
(
[0] => two
[1] => Array
(
[0] => 4
[1] => 5
[2] => 6
)
)
[2] => Array
(
[0] => three
[1] => Array
(
[0] => 7
[1] => 8
[2] => 9
)
)
)
I was trying to use the foreach function but I cannot seem to get this to work. This was my original syntax (though I realise it is wrong).
$tmpArray = array(array("one",array(1,2,3)),array("two",array(4,5,6)),array("three",array(7,8,9)));
foreach ($tmpArray[1] as $value) {
echo $value;
}
I was trying to avoid a variable compare on whether the key is the same as the key I want to search, i.e.
foreach ($tmpArray as $key => $value) {
if ($key == 1) {
echo $value;
}
}
Any ideas?
If you know the number of levels in nested arrays you can simply do nested loops. Like so:
// Scan through outer loop
foreach ($tmpArray as $innerArray) {
// Check type
if (is_array($innerArray)){
// Scan through inner loop
foreach ($innerArray as $value) {
echo $value;
}
}else{
// one, two, three
echo $innerArray;
}
}
if you do not know the depth of array you need to use recursion. See example below:
// Multi-dementional Source Array
$tmpArray = array(
array("one", array(1, 2, 3)),
array("two", array(4, 5, 6)),
array("three", array(
7,
8,
array("four", 9, 10)
))
);
// Output array
displayArrayRecursively($tmpArray);
/**
* Recursive function to display members of array with indentation
*
* #param array $arr Array to process
* #param string $indent indentation string
*/
function displayArrayRecursively($arr, $indent='') {
if ($arr) {
foreach ($arr as $value) {
if (is_array($value)) {
//
displayArrayRecursively($value, $indent . '--');
} else {
// Output
echo "$indent $value \n";
}
}
}
}
The code below with display only nested array with values for your specific case (3rd level only)
$tmpArray = array(
array("one", array(1, 2, 3)),
array("two", array(4, 5, 6)),
array("three", array(7, 8, 9))
);
// Scan through outer loop
foreach ($tmpArray as $inner) {
// Check type
if (is_array($inner)) {
// Scan through inner loop
foreach ($inner[1] as $value) {
echo "$value \n";
}
}
}
foreach ($tmpArray as $innerArray) {
// Check type
if (is_array($innerArray)){
// Scan through inner loop
foreach ($innerArray as $value) {
echo $value;
}
}else{
// one, two, three
echo $innerArray;
}
}
Both syntaxes are correct. But the result would be Array. You probably want to do something like this:
foreach ($tmpArray[1] as $value) {
echo $value[0];
foreach($value[1] as $val){
echo $val;
}
}
This will print out the string "two" ($value[0]) and the integers 4, 5 and 6 from the array ($value[1]).
As I understand , all of previous answers , does not make an Array output,
In my case :
I have a model with parent-children structure (simplified code here):
public function parent(){
return $this->belongsTo('App\Models\Accounting\accounting_coding', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Models\Accounting\accounting_coding', 'parent_id');
}
and if you want to have all of children IDs as an Array , This approach is fine and working for me :
public function allChildren()
{
$allChildren = [];
if ($this->has_branch) {
foreach ($this->children as $child) {
$subChildren = $child->allChildren();
if (count($subChildren) == 1) {
$allChildren [] = $subChildren[0];
} else if (count($subChildren) > 1) {
$allChildren += $subChildren;
}
}
}
$allChildren [] = $this->id;//adds self Id to children Id list
return $allChildren;
}
the allChildren() returns , all of childrens as a simple Array .
I had a nested array of values and needed to make sure that none of those values contained &, so I created a recursive function.
function escape($value)
{
// return result for non-arrays
if (!is_array($value)) {
return str_replace('&', '&', $value);
}
// here we handle arrays
foreach ($value as $key => $item) {
$value[$key] = escape($item);
}
return $value;
}
// example usage
$array = ['A' => '&', 'B' => 'Test'];
$result = escape($array);
print_r($result);
// $result: ['A' => '&', 'B' => 'Test'];
array(
"IT"=>
array(
array('id'=>888,'First_name'=>'Raahul','Last_name'=>'Pandey'),
array('id'=>656,'First_name'=>'Ravi','Last_name'=>'Teja'),
array('id'=>998,'First_name'=>'HRX','Last_name'=>'HRITHIK')
),
// array(
"DS"=>
array(
array('id'=>87,'First_name'=>'kalia','Last_name'=>'Pandey'),
array('id'=>6576,'First_name'=>'Raunk','Last_name'=>'Teja'),
array('id'=>9987,'First_name'=>'Krish','Last_name'=>'HRITHIK')
)
// )
)
);
// echo "";
// print_r($a);
echo "";
print_r($a);
?>
////how to get id in place of index value....