Search code examples
phparraysmultidimensional-arraygroupingsub-array

Group rows of a 2D array by a column


I have a .csv upload form that takes the values from the file and formats them appropriately. Or at least that is what I am trying to accomplish.

What I am trying to do is to format the 6 index of every inside array with the name that is held within, example:

Array (
  [0] => Array (
    [0] => 2
    [1] => 230
    [2] => 12 Aug 2013 07:46 AM
    [3] => 816
    [4] => INB
    [5] => 1.38
    [6] => Leonard
  )
)
Array (
  [0] => Array (
    [0] => 2
    [1] => 230
    [2] => 12 Aug 2013 07:46 AM
    [3] => 816
    [4] => INB
    [5] => 1.38
    [6] => Leonardo
  ) 
  [1] => Array(
    [0] => 3
    [1] => 170
    [2] => 12 Aug 2013 07:47 AM
    [3] => 170
    [4] => OUT (EXTENSION)
    [5] => 1.08
    [6] => Greg
  )
)

So this .csv file will contain thousands of these entries but I am trying to format it so that all of the Leonardo's will be clumped together rather than 1 here and there and than a 100 rows down in the .csv file that is sent out to download.

Firstly let me explain how the data is retrieved. A while loop opens the file and iterates through the entire file. Data is analysed and than checked, each array is stored into this:

$collectedData[$i] = $tmpfileop;
$i++;

Here is what I am doing now:

        #FUNCTION TO CLEAN DATA
function cleanData(&$str)
{
   // escape tab characters
    $str = preg_replace("/\t/", "\\t", $str);

    // escape new lines
    $str = preg_replace("/\r?\n/", "\\n", $str);

    // convert 't' and 'f' to boolean values
    if($str == 't') $str = 'TRUE';
    if($str == 'f') $str = 'FALSE';

    // force certain number/date formats to be imported as strings
    if(preg_match("/^0/", $str) || preg_match("/^\+?\d{8,}$/", $str) || preg_match("/^\d{4}.\d{1,2}.\d{1,2}/", $str)) {
      $str = "'$str";
    }

    // escape fields that include double quotes
    if(strstr($str, '"')) $str = '"' . str_replace('"', '""', $str) . '"';
    
}

  $filename = "call_logs_as_of_" . date('Ymd') . ".xls";
    
  header("Content-Disposition: attachment; filename=\"$filename\"");
  header("Content-Type: application/vnd.ms-excel");
    
  $flag = false;
  foreach($collectedData as $row) {
    if(!$flag) {
      // display field/column names as first row
      echo implode("\t", array_keys($row)) . "\r\n";
      $flag = true;
    }
     array_walk($row, 'cleanData');
    echo implode("\t", array_values($row)) . "\r\n";
  }

This works great, it's just not formatted correctly and I am confused on how to accomplish what I am trying to do.


Solution

  • The following will aggregate your data into an associative array keyed by the 6th field in your original arrays:

    $normalized = array();
    foreach($collecteddata as $row) {
      if( !isset($normalized[$row[6]] ) {
        $normalized[$row[6]] = array();
      }
      $normalized[$row[6]][] = $row;
    }
    

    Note that this will double your memory usage. It would be better to implement this where you are reading in the CSV in the first place.

    Also, when you post a question please format your data nicely. The original formatting of your array was like looking into a bowl of alphabet soup. :P