Search code examples
phpcsvassociative-array

PHP unordered associative array to CSV line matching headers to keys, creating new headers as needed


I have a series of arrays, like similar:

$my_array = {"foo"=>"bar", "sudo"=>"make_sandwhich", "bam"=>"bang"};

and a csv like:

     Header 1, Header 2, foo, Header 4  , bam      , Header 5
     Lorem.  , Ipsum.  ,    , cheesecake, Henrietta, Loreal
             ,         ,    ,           ,          , New Hampshire

And I want the csv to look like this when I add the example line from the start...

     Header 1, Header 2, foo, Header 4  , bam      , Header 5     , sudo
     Lorem.  , Ipsum.  ,    , cheesecake, Henrietta, Loreal       ,
             ,         ,    ,           ,          , New Hampshire,
             ,         , bar,           , bang     ,              , make_sandwhich

I've tried messing with the fputcsv, but it doesn't seem to have anything near the functionality I need. Is there a way to do this?


Solution

  • It can be achieved with fgetcsv and fputcsv and "some" additional processing.

    Read your csv file (first row are the headers), then add the new headers from your input row. Then read the rows, create missing data for new headers and add a new row with all the headers at the end. Then just write that data back to csv file.

    a.csv - input:

         Header 1, Header 2, foo, Header 4  , bam      , Header 5
         Lorem.  , Ipsum.  ,    , cheesecake, Henrietta, Loreal
                 ,         ,    ,           ,          , New Hampshire
    

    code:

    <?php
    
    $myArray = ["foo"=>"bar", "sudo"=>"make_sandwhich", "bam"=>"bang"];
    
    $readHandle = fopen("a.csv", "r");
    
    // get headers (first row)
    $headers = fgetcsv($readHandle);
    
    $allHeaders = [];
    foreach ($headers as $header) {
      $allHeaders[] = trim($header);
    }
    
    // add missing headers from the input data (so to headers from file add sudo header)
    foreach (array_keys($myArray) as $data) {
      if (!in_array($data, $allHeaders)) {
        $allHeaders[] = $data;
      }
    }
    var_dump($allHeaders);
    
    // read all rows from the file and add the new headers
    $rows = [];
    while($row = fgetcsv($readHandle)) {
      foreach ($allHeaders as $k => $header) {
        $row[$k] = isset($row[$k]) ? trim($row[$k]) : '';
      }
    
      $rows[] = $row;
    }
    
    // add the new row from input data
    $newRow = [];
    foreach ($allHeaders as $header) {
      $newRow[] = $myArray[$header] ?? '';
    }
    $rows[] = $newRow;
    var_dump($rows);
    
    fclose($readHandle);
    
    // now write headers and rows to csv file
    $writeHandle = fopen('b.csv', 'w+');
    
    fputcsv($writeHandle, $allHeaders);
    
    foreach ($rows as $row) {
      fputcsv($writeHandle, $row);
    }
    
    fclose($writeHandle);
    

    $allHeaders:

    array(7) {
      [0]=>
      string(8) "Header 1"
      [1]=>
      string(8) "Header 2"
      [2]=>
      string(3) "foo"
      [3]=>
      string(8) "Header 4"
      [4]=>
      string(3) "bam"
      [5]=>
      string(8) "Header 5"
      [6]=>
      string(4) "sudo"
    }
    

    $rows:

    array(3) {
      [0]=>
      array(7) {
        [0]=>
        string(6) "Lorem."
        [1]=>
        string(6) "Ipsum."
        [2]=>
        string(0) ""
        [3]=>
        string(10) "cheesecake"
        [4]=>
        string(9) "Henrietta"
        [5]=>
        string(6) "Loreal"
        [6]=>
        string(0) ""
      }
      [1]=>
      array(7) {
        [0]=>
        string(0) ""
        [1]=>
        string(0) ""
        [2]=>
        string(0) ""
        [3]=>
        string(0) ""
        [4]=>
        string(0) ""
        [5]=>
        string(13) "New Hampshire"
        [6]=>
        string(0) ""
      }
      [2]=>
      array(7) {
        [0]=>
        string(0) ""
        [1]=>
        string(0) ""
        [2]=>
        string(3) "bar"
        [3]=>
        string(0) ""
        [4]=>
        string(4) "bang"
        [5]=>
        string(0) ""
        [6]=>
        string(14) "make_sandwhich"
      }
    }
    

    b.csv - output:

    "Header 1","Header 2",foo,"Header 4",bam,"Header 5",sudo
    Lorem.,Ipsum.,,cheesecake,Henrietta,Loreal,
    ,,,,,"New Hampshire",
    ,,bar,,bang,,make_sandwhich
    

    I hope you dont want the spaces in csv for formatting. It can also be done but you need to keep the amount of characters for the longest string in each column and pad the values to that lengths.