Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm trying to build a tool to average values from a multidimensional array in PHP. Array sample:

$t[1][1]=2;
$t[1][2]=3;
$t[2][1]=5;
$t[3]=6;
$t[4][1][1]=9;
$t[4][1][2]=10;
$t[4][2][1]=12;
$t[4][2][2]=13;

Notice, too, that parents have no value (Since they have children). I have this:

function chklevel($s) {
  $l = explode(".",$s);
}

Which gives me the ability to call chklevel as

chklevel("4.2.2")

and have it return 13, but I also want to be able to call

chklevel("4")

and have it return 11 (Which is the average of 4.1.1(9), 4.1.2(10), 4.2.1(12) and 4.2.2(13).

Any thoughts?

share|improve this question
    
How can you call chklevel(4.2.2) when the parameter is not a string? This will throw an error. Also, the function chklevel doesn't return anything yet, is that intentional? Maybe I just don't get your question good enough. –  Czechnology Mar 5 '11 at 21:16
    
Sorry - corrected. I could have sworn I had the quotes in there. Also, it's more of a pseudocode than true code, as I've got an explode() handling the string for me. –  tommyvallier Mar 5 '11 at 21:18
    
make sure you see my update to the answer, I added an array check (your 4.2.2 example was giving me trouble as an array wasn't passed (a value was) so the foreach bombed--but it's fixed now) –  Brad Christie Mar 5 '11 at 21:29

2 Answers 2

up vote 1 down vote accepted

I had to do it in two functions (just because of the recursive nature of the search, but here's my bid:

function mdarray_avg($array, $level = ''){
  if (!is_array($array)) throw new Exception("First argument must be an array");
  if (!is_string($level)) throw new Exception("Second argument must be string");

  $ary = $array;
  $levels = explode('.',$level);
  foreach ($levels as $lvl){
    if (array_key_exists($lvl,$ary))
      $ary = $ary[$lvl];
    else
      throw new Exception("Level {$level} doesn't exist");
  }

  $numbers = _mdarray_avg($ary);
  $sum = array_sum($numbers);
  return ($sum > 0 ? $sum / count($numbers) : 0);
}

function _mdarray_avg($array){
  if (!is_array($array)) return array($array);
  $numbers = array();
  foreach ($array as $element){
    if (is_array($element))
      $numbers = array_merge($numbers,_mdarray_avg($element));
    else
      $numbers[] = $element;
  }
  return $numbers;
}

Use it like so:

echo "Average: ".mdarray_avg($t,'4'); // returns 11, as expected. 

Where $t is the array to search through, and the '4' is the level you're searching (could also be '1', '4.2', etc.

Also, a fun note, exempting the second parameter averages the whole array, in this case I returned 7.5 (avg(2,3,5,6,9,10,12,13))

share|improve this answer
    
That was brilliant. Thanks. :) –  tommyvallier Mar 5 '11 at 21:28
    
@tommyvallier: not a problem. Also added another checksum against an invalid level (instead of silently skipping over a missing array key it will exception out saying the level doesn't exist [previously using 6.4.2 would actually be averaging 4.2. It wouldn't find level 6, would skip over, then continue on to look for 4]) –  Brad Christie Mar 5 '11 at 21:32

Here's my solution:

function chklevel($t) {

  for ($i = 1; $i < func_num_args(); $i++) {
    if (is_array($t) && isset($t[func_get_arg($i)]))
      $t = $t[func_get_arg($i)];
  }

  return GetAvg($t);
}

function GetAvg($arr) {

  if (is_array($arr)) {
    $avg = 0;
    foreach ($arr as $v) {
      $avg += GetAvg($v);
    }
    $avg /= count($arr);

  } else if (is_numeric($arr)) {
    $avg = $arr;
  }

  return $avg;
}

I prefer function call like this: chklevel($t, 4,2,2); but you could easily modify it for strings:

function chklevel($t, $s) {
  $indexes = explode(".", $s);
  for ($i = 0; $i < count($indexes); $i++) {
    if (is_array($t) && isset($t[$indexes[$i]]))
      $t = $t[$indexes[$i]];
  }

  return GetAvg($t);
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.