I'm using PHP 5.6, and I have the following array:
Array (
[0] => Array (
[id] => 1
[name] => James
)
[1] => Array (
[id] => 2
[name] => Tim
[children] => Array (
[0] => Array (
[id] => 4
[name] => Sam
)
[1] => Array (
[id] => 5
[name] => Florence
)
)
)
[2] => Array (
[id] => 3
[name] => Stephen
)
)
I'm trying to find a neat and fast way to count the number of people, which is the same as counting the number of numeric keys, which should be 5.
echo count($myarray); // 3 (only first level)
echo count($myarray, COUNT_RECURSIVE); // 16 (every key/value)
Is there a good way to do this with built-in PHP functions, or do I need to traverse the whole multidimensional array and count them manually..?
EDIT My array could end up being 1,000+ people (or more), with many many levels (an unknown number of levels).
It is important to note that, even if there were a PHP built-in (such as count($myarray, COUNT_RECURSIVE_NUMERIC);) internally, it would still be traversing the whole array, recursively. If you are worried about Out Of Memory errors, try pass-by-reference, which will not copy the array or the array items:
define('COUNT_RECURSIVE', 1);
function count_numeric_keys(&$array, $flags = 0) {
$count = 0;
foreach ($array as $key => $value) {
$count += (int) (is_numeric($key));
if ($flags & COUNT_RECURSIVE && is_array($value)) {
$count += count_numeric_keys($value, $flags);
}
}
return (int) $count;
}
$count = count_numeric_keys($array, COUNT_RECURSIVE);
Mayhaps?
Comparison with non-pass-by-reference, type-hint, and small benchmark:
define('COUNT_RECURSIVE', 1);
function count_numeric_keys(Array &$array, $flags = 0) {
$count = 0;
foreach ($array as $key => $value) {
$count += (int) (is_numeric($key));
if ($flags & COUNT_RECURSIVE && is_array($value)) {
$count += count_numeric_keys($value, $flags);
}
}
return (int) $count;
}
function count_numeric_keys_np(Array $array, $flags = 0) {
$count = 0;
foreach ($array as $key => $value) {
$count += (int) (is_numeric($key));
if ($flags & COUNT_RECURSIVE && is_array($value)) {
$count += count_numeric_keys_np($value, $flags);
}
}
return (int) $count;
}
$tpl_array = array(
1=>"one",
"two"=>"two",
3=>array(
1=>1,
"two"=>2
)
);
// Fill the array with both numeric and non-numeric
$array = array();
for($i = 1000; $i > 0; $i--) {
$array[] = $tpl_array;
}
for($i = 1000; $i > 0; $i--) {
$array["not a number $i"] = $tpl_array;
}
echo "Pre Memory: ".memory_get_usage(TRUE).PHP_EOL;
echo "Count: ".count_numeric_keys($array, COUNT_RECURSIVE).PHP_EOL;
echo "Reference Memory: ".memory_get_usage(TRUE)." current, ".memory_get_peak_usage(TRUE)." peak.\n";
count_numeric_keys_np($array, COUNT_RECURSIVE);
echo "No-Reference Memory: ".memory_get_usage(TRUE)." current, ".memory_get_peak_usage(TRUE)." peak.\n";
View it on IDEONE here.
ODDLY, having a reference on $value, like foreach($array as $key => &value) {} actually increased memory usage. Bizarre...
I just created this recursive function to do this for ya :P
function countNumericKeys($array)
{
$count = 0;
foreach ($array as $key => $value)
{
if (is_numeric($key))
{
$count ++;
}
if (is_array($value))
{
$count += countNumericKeys($value);
}
}
return $count;
}
// Test it!
$array = [
1=>"one",
"two"=>"two",
3=>[
1=>1,
"two"=>2
]
];
print countNumericKeys($array); // Output: 3, correct (in this case at least)!
Shortened the code so it uses ternary operators instead of the ifs that it was :P
function simpleCountNumericKeys($array)
{
$count = 0;
foreach ($array as $key => $value)
{
$count += is_numeric($key) ? 1 : 0;
$count += is_array($value) ? simpleCountNumericKeys($value) : 0;
}
return $count;
}
TEST USING array_keys -- only gets the top level keys of the array
function countArrayKeysNumeric($array)
{
$count = 0;
$keys = array_keys($array);
foreach ($keys as $key) $count += is_numeric($key) ? 1 :0;
return $count;
}
$array = [
1=>"one",
"two"=>"two",
3=>[
1=>1,
"two"=>2
]
];
print countArrayKeysNumeric($array);
Prints 2... :(
Related
From the code below I can compare the 2 arrays and find out the $subset elements position range in $array.
$array = [8,2,3,7,4,6,5,1,9];
$subset = [6,3,7];
function get_range($array, $subset)
{
$min = sizeof($array);
$max = 0;
foreach($subset as $value) {
$occurrence = array_search($value, $array);
if( $occurrence < $min ) {
$min = $occurrence;
}
if( $occurrence > $max ) {
$max = $occurrence;
}
}
return [$min, $max];
}
$range = get_range($array, $subset); // result as an array -> [2, 5]
However, I want to do a recursive array_search for my multidimentional array like:
$subset = array (
array(6,3,7),
array(4,2,9),
array(3,5,6),
);
How can I do this? Expecting results -> [2, 5], [1, 8], [2, 6].
You do not need a recursion here, simply add an additional loop in the get_range() function. The following example is based on your code and is a possible solution to your problem:
<?php
$array = [8,2,3,7,4,6,5,1,9];
$subsets = array (
array(6,3,7),
array(4,2,9),
array(3,5,6),
);
function get_range($array, $subsets)
{
$result = array();
foreach ($subsets as $subset) {
$min = sizeof($array);
$max = 0;
foreach($subset as $value) {
$occurrence = array_search($value, $array);
if( $occurrence < $min ) {
$min = $occurrence;
}
if( $occurrence > $max ) {
$max = $occurrence;
}
}
$result[] = [$min, $max];
}
return $result;
}
$range = get_range($array, $subsets);
echo print_r($range, true);
?>
Result:
Array (
[0] => Array ( [0] => 2 [1] => 5 )
[1] => Array ( [0] => 1 [1] => 8 )
[2] => Array ( [0] => 2 [1] => 6 )
)
I have one array like this :
$array='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}'
I want first split to two array like this :
$array[0]={b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1}
And
$array[1]={b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}
I change every array with this code
foreach ($b as $k => $m) {
if ($k % 2 == 0) {
$even[]= $m;
}
else {
$odd[] = $m;
}
}
$ff=array_combine($even,$odd);
I want output change like this
Array( Array[0] => ([b_price] => 9500 [b_discount] => 10 [mainPrice] => 95000 [total] => 95000 [title] =>obj1)
Array[1] => ([b_price] => 1500 [b_discount] => 15 [mainPrice] => 15000 [total] => 22500 [title] => obj2))
Two approaches:
-- using explode and array_map functions:
$str = '{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
$result = array_map(function($v){
$r = [];
$arr = explode(',', trim($v, '{}'));
foreach ($arr as $k => $v) {
if (!($k % 2)) $r[$v] = $arr[$k+1];
}
return $r;
}, explode('},{', $str));
print_r($result);
-- using additional preg_match_all and array_combine functions:
$str = '{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
$result = array_map(function($v){
preg_match_all('/([^,]+),([^,]+),?/', trim($v, '{}'), $m);
return array_combine($m[1], $m[2]);
}, explode('},{', $str));
print_r($result);
The output:
Array
(
[0] => Array
(
[b_price] => 9500
[b_discount] => 10
[mainPrice] => 95000
[total] => 95000
[title] => obj1
)
[1] => Array
(
[b_price] => 1500
[b_discount] => 15
[mainPrice] => 15000
[total] => 22500
[title] => obj2
)
)
you should change your needle, in your array string,
i have changed it with semicolon,
$arrayString='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1};{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
echo $arrayString;
echo "<pre>"; print_r (explode(";",$arrayString));
$b=explode(";",$arrayString);
foreach ($b as $k => $m) {
if ($k % 2 == 0) {
$even[]= $m;
}
else {
$odd[] = $m;
}
}
$ff=array_combine($even,$odd);
So, I write this decision. Maybe it can be more clear, but it works.
$array='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
/*Making array from string*/
$tmp_array = explode("},{", $array);
/*Removing { symbols*/
$tmp_array[0] = substr($tmp_array[0],1);
$tmp_array[1] = substr($tmp_array[1],0,-1);
/*Making arrays from string [0] and [1]*/
$tmp_array[0] = explode(',',$tmp_array[0]);
$tmp_array[1] = explode(',',$tmp_array[1]);
$new_array = [];
/*Creating associative arrays*/
for($a = 0; $a < count($tmp_array); $a++) {
$new_as_array = [];
for($i = 0; $i <= count($tmp_array[0]); $i+=2) {
if($i + 1 <= count($tmp_array[0])) {
$new_as_array[$tmp_array[$a][$i]] = $tmp_array[$a][$i + 1];
}
}
$new_array[] = $new_as_array;
}
print_r($new_array);
Today in an interview i got the following question to solve without using any inbuilt function eg in_array and etc.I am able to solve the programme but they told me is there any better approach and also they told me the total code is only upto 7 lines.So can anyone tell me any better approach than this:-
<?php
$a = array(1,3,5,2,1,5,11,16);
$b = array(1,4,3,11,12,5,7,18);
$final = [];
for($i=0;$i<count($a);$i++){
$flag = 0;
if($i==0){
$final[] = $a[$i];
} else {
for($j=0;$j<count($final);$j++){
if($a[$i] == $final[$j]){
$flag = 1;
}
}
if($flag==0){
$final[] = $a[$i];
}
for($k=0;$k<count($final);$k++){
if($b[$i] == $final[$k]){
$flag = 1;
}
}
if($flag==0){
$final[] = $b[$i];
}
}
}
echo '<pre>';
print_r($final);
the result would be the final array which would contain unique array of both eg
Array ( [0] => 1 [1] => 3 [2] => 4 [3] => 5 [4] => 2 [5] => 11 [6] => 16 [7] => 18 )
Here is how you could do it without using built-in functions: Collect the values as keys (so they are unique), and then put those keys back as values in the final array:
$a = array(1,3,5,2,1,5,11,16);
$b = array(1,4,3,11,12,5,7,18);
foreach($a as $v) $keys[$v] = 1;
foreach($b as $v) $keys[$v] = 1;
foreach($keys as $k => $v) $final[] = $k;
echo '<pre>';
print_r($final);
See it run on eval.in.
Just set the key as the value and they will overwrite. If the arrays are the same length:
foreach($a as $k => $v) {
$final[$v] = $v;
$final[$b[$k]] = $b[$k];
}
If not, then do it for each array:
foreach(array($a, $b) as $c) {
foreach($c as $v) {
$final[$v] = $v;
}
}
The order and keys won't be the same, but that wasn't stated as a requirement.
I have following function to sum multidimensional array values.
// summing values of multidimensional array
function getSum($array, $path = array()){
// process second argument:
foreach ($path as $key) {
if (!is_array($array) || !isset($array[$key])) {
return 0; // key does not exist, return 0
}
$array = $array[$key];
}
if(is_array($array)) {
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$sum = 0;
foreach ($iterator as $key => $value) {
$sum += $value;
}
} else{
$sum = $array;
}
return $sum;
}
I'm using the function like this:
$array = array();
$array['one']['green'][20] = 20;
$array['one']['blue'][20] = 5;
$array['one']['blue'][30] = 10;
getSum($array,['one','green']); // 20
getSum($array,['one','blue',20]); // 5
Now I have a problem if I don't want to for example set any spesific color because I want that script sums all values from category 20 from all colours.
So it should be working like this:
getSum($array,['one','*',20]); // 25
Thanks for your help!
Here is example of my array:
Array (
[1] => Array (
[AREA I] => Array (
[20] => 1
[25] => 0
[30] => 0 )
[AREA II] => Array (
[20] => 0
[30] => 0 )
[AREA III] => Array (
[20] => 2
[30] => 0 )
[AREA IV] => Array (
[20] => 0
[30] => 3 )
[AREA V] => Array (
[20] => 4
[25] => 0
[30] => 3 )
)
[2] => Array (
[AREA I] => Array (
[20] => 0
[30] => 0 )
[AREA II] => Array (
[20] => 0
[30] => 0 )
)
)
And here is example of my getSum call:
getSum($visitsandinfosact,['*','*',20]); // should print 7
Recursive Function
I was not sure if ['one','*'] should give 45 but if it should just return 0 you just have to remove the else if (empty($filterList) && is_array($value) && $first == "*")condition. All values which are not arrays are just converted to int via intval and added to the sum. If you wanna use float then use floatval instead of intval
function getSum($array, $filterList = array('*')) {
$sum = 0;
$first = array_shift($filterList);
foreach ($array as $key => $value) {
if ($key == $first || $first == "*") {
if (is_array($value) && !empty($filterList)) {
$sum += getSum($value, $filterList);
} else if (empty($filterList) && is_array($value)) {
$sum += getSum($value, array("*"));
} else if (empty($filterList)) {
$sum += intval($value);
}
}
}
return $sum;
}
echo getSum($array,['one','*',20], 10) . "\n"; // 25
echo getSum($array,['one','*','*',20]) . "\n"; // 10
echo getSum($array,['one','*']) . "\n"; // 45
echo getSum($array) . "\n"; // 45
Input Array
$array = array();
$array['one'] = array();
$array['one']['green'] = array();
$array['one']['green'][20] = 20;
$array['one']['blue'] = array();
$array['one']['blue'][20] = 5;
$array['one']['blue'][30] = 10;
$array['one']['orange']['red'][20] = 10;
Output
Only the numbers are outputted but just added the input params for better understanding.
25 // (['one','*',20])
10 // (['one','*','*',20])
45 // (['one','*'])
45 // no filterList
In short, you need a recursive function to add in wildcard "endpoints". You might as well use the same recursive nature to cover the addition as well.
The following should do what you're wanting:
// summing values of multidimensional array
function getSum(&$array, $path = array()){
$sum = 0;
if(is_int($array) and empty($path)) // return value if int
$sum = $array;
else if(is_array($array)){ // else add recurred values
if(empty($path)){
foreach($array as $value)
$sum += getSum($value);
} else {
$key = array_shift($path);
if($key=='*'){
foreach($array as $value)
$sum += getSum($value, $path);
} else {
if(isset($array[$key]))
$sum += getSum($array[$key], $path);
}
}
}
return $sum;
}
Test:
$array['one'] = array();
$array['one']['green'] = array();
$array['one']['green'][20] = 20;
$array['one']['blue'] = array();
$array['one']['blue'][20] = 5;
$array['one']['blue'][30] = 10;
$array['one']['orange']['red'][20] = 10;
echo getSum($array,['one','*',20]); // 25
echo getSum($array,['one','*','*',20]); // 10
echo getSum($array,['one','*']); // 45
Happy coding
iam trying to build a multidimensional array.
public function saveRateTemplateData($RateTemplateInfo)
{
$RateTemplateID = $RateTemplateInfo['id'];
$Zones = $RateTemplateInfo['premium_zones'];
//$ZoneZipCodeIDs[] = array();
for ($n = 1; $n <= $RateTemplateInfo['premium_zones']; $n++) {
$ZoneNum = 'zone' . $n;
$ZipCodeArray = explode(",",$_POST[$ZoneNum]);
$ZipCodeIDs=array();
foreach ($ZipCodeArray as $v) {
$v = intval(trim($v));
if (strlen($v) == 5) {
array_push($ZipCodeIDs, $this->addZipCode($v));
} else {
echo "it isnt 5";
}
}
}
}
so what iam trying to do is make an array of an array. so this is how its supposed to look
Array
(
[1] => Array
(
[0] => 34
[1] => 31
[2] => 23
)
[2] => Array
(
[0] => 18
[1] => 4
[2] => 35
[3] => 1
)
)
i have tried numerous ways it doesnt work
basically i want it in this format VarName[ZoneNumbers][ZipCodeID]
so i can loop through it later on. so i can print like this $VarName[$n] then a array of all zipcodeID will print for Zone Number 1 in this case it will print 34,31,23
public function saveRateTemplateData($RateTemplateInfo)
{
$RateTemplateID = $RateTemplateInfo['id'];
$zones = array(); // you weren't using this so I'll use it to hold the data
for ($n = 1; $n <= $RateTemplateInfo['premium_zones']; $n++) {
$ZoneNum = 'zone' . $n;
// create an array under the zone number for holding the IDs
$zones[$n] = array();
$ZipCodeArray = explode(",",$_POST[$ZoneNum]);
foreach ($ZipCodeArray as $v) {
$v = (int) trim($v);
if (strlen($v) == 5) {
$zones[$n][] = $this->addZipCode($v);
} else {
// use exceptions for exceptional circumstances
throw new RuntimeException(sprintf('Invalid zone ID "%s"', $v));
}
}
}
return $zones;
}