Friday, October 09, 2009

PHP eval is powerful indeed!

I have this array probem.
A set of data
$data = array(
  array("NAME"=>"Person A", "PROJECT"=>1, "COUNTRY"=>1, "HOURS"=>40),
  array("NAME"=>"Person A", "PROJECT"=>1, "COUNTRY"=>1, "HOURS"=>30),
  array("NAME"=>"Person A", "PROJECT"=>1, "COUNTRY"=>2, "HOURS"=>70),
  array("NAME"=>"Person A", "PROJECT"=>2, "COUNTRY"=>2, "HOURS"=>10),
  array("NAME"=>"Person A", "PROJECT"=>3, "COUNTRY"=>1, "HOURS"=>50),
  array("NAME"=>"Person B", "PROJECT"=>1, "COUNTRY"=>2, "HOURS"=>40),
  array("NAME"=>"Person B", "PROJECT"=>1, "COUNTRY"=>2, "HOURS"=>10),
  array("NAME"=>"Person B", "PROJECT"=>2, "COUNTRY"=>2, "HOURS"=>50),
  );
which i want in this format
$expecteddata1 = array(
  array("NAME"=>"Person A", "HOURS"=>200),
  array("NAME"=>"Person B", "HOURS"=>100),
);
and also this,
$expecteddata2 = array(
  array("NAME"=>"Person A", "PROJECT"=>1, "HOURS"=>140),
  array("NAME"=>"Person A", "PROJECT"=>2, "HOURS"=>10),
  array("NAME"=>"Person A", "PROJECT"=>3, "HOURS"=>50),
  array("NAME"=>"Person B", "PROJECT"=>1, "HOURS"=>50),
  array("NAME"=>"Person B", "PROJECT"=>2, "HOURS"=>50),
);
The expected data need grouping among multiple numbers of options. Note the Total.

It took some time to come with the solution after working a bit with the powerful eval() function.

It's much easy to do the looping and grouping as follows
for($data as $rowid=>$row) {
  $newdata[$row["NAME"]] += $row["HOURS]; //gives the data required for $expecteddata1
  $newdata[$row["NAME"]][$row["PROJECT]] += $row["HOURS]; //gives the data required for $expecteddata2
}
which can be made to work for any number of options with
for first case $byOptions = array("NAME");
and for second case $byOptions = array("NAME", "PROJECT");
$str = "";
  foreach($byOptions as $option) {
    $str .= '[$row['.$option.']]';
  }
  $data = array();
  foreach($input as $rowid=>$row) {
    eval("@\$data".$str."['hours'] += ".$row['HOURS'].";");
  }

and next loop through $newdata and generate the expecteddata. This part of code needs to be refactored. If any of you can come up with good solution to remove the options dependent looping, it would be great. [update: I came up with solution in next post that eliminates the options dependent looping using eval]

function groupData($input, $byOptions) {
  $str = "";
  foreach($byOptions as $option) {
    $str .= '[$row['.$option.']]';
  }
  $data = array();
  foreach($input as $rowid=>$row) {
    eval("@\$data".$str."['hours'] += ".$row['HOURS'].";");
  }
  
  $newdata = array();
  foreach($data as $id=>$arr) {
    if(isset($arr['hours'])) {
      $newdata[] = array($byOptions[0]=>$id, 'HOURS'=>$arr['hours']);
    } else {
      foreach($arr as $id1=>$arr1) {
        if(isset($arr1['hours'])) {
          $newdata[] = array($byOptions[0]=>$id, $byOptions[1]=>$id1, 'HOURS'=>$arr1['hours']);      
        } else {
          foreach($arr1 as $id2=>$arr2) {
            $newdata[] = array($byOptions[0]=>$id, $byOptions[1]=>$id1, $byOptions[2]=>$id2, 'HOURS'=>$arr2['hours']);     
          }
        }
      }
    }
  }  
  return $newdata;
}

Test the function. It should work.
$output1 = groupData($data, array("NAME"));
print ($expecteddata1 == $output1)?"Same":"Different Objects";

$output2 = groupData($data, array("NAME", "PROJECT"));
print ($expecteddata2== $output2)?"Same":"Different Objects";  

No comments: