2

I have a multidimensional array in which the first level keys needs to be the first line of data in .csv string.

The rows of indexed elements need to be transpose and converted into subsequent lines of csv text.

The output will be identical to a .csv file's contents, but I don't want a file to be created, just as a .csv string.

Sample data:

$data = [
     'dates' => ['2010-01-02', '2011-02-03', '2011-02-04'],
     'type1' => ['data1', 'data2', 'data3'],
     'type2' => ['data4', 'data5', 'data6']
];

Desired output string:

dates,type1,type2
2010-01-02,data1,data4
2011-02-03,data2,data5
2011-02-04,data3,data6
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
julio
  • 6,630
  • 15
  • 60
  • 82

5 Answers5

1

Something like this would probably come close to what you want (not tested, just going off the top of my head):

$fh = fopen('file.csv', 'w');

// write out the headers
fputcsv($fh, array_keys($data));

// write out the data    
for ($i = 0; $i < count($data['dates']); $i++) {
    $temp = array($data['dates'][$i], $data['type1'][$i], $data['type2'][$i]);
    fputcsv($fh, $temp);
}
Marc B
  • 356,200
  • 43
  • 426
  • 500
  • thanks Marc-- that's sort of what I would have done but I need it as a string, not as a file. – julio Jul 06 '11 at 16:57
  • @marc-- thanks, but I am using this to feed data to a function, and don't want to leave data files laying around (or deal with cleanup). – julio Jul 06 '11 at 17:07
  • 1
    Open the file in memory instead on disk: `fopen('php://memory', 'rw')` – hakre Jul 06 '11 at 19:59
0

This is much more verbose than you'll likely ever need, but it allows you to have an array of n elements. zip is based off of the Python function of the same name. I did not write it.

$data = ($data);
$r = (call_user_func_array('zip', $data));

$fin = "";
$fin .= implode(',',array_keys($data)).PHP_EOL;
foreach($r as $arr)
{
    $fin .= implode(',',$arr).PHP_EOL;
}

// $fin now holds all the data. Do something with it and you're finished!

function zip() {
    $args = func_get_args();
    $zipped = array();
    $n = count($args);
    for ($i=0; $i<$n; ++$i) {
        reset($args[$i]);
    }
    while ($n) {
        $tmp = array();
        for ($i=0; $i<$n; ++$i) {
            if (key($args[$i]) === null) {
                break 2;
            }
            $tmp[] = current($args[$i]);
            next($args[$i]);
        }
        $zipped[] = $tmp;
    }
    return $zipped;
}

(check out the conversation in the comments. It's really relevant.)

Community
  • 1
  • 1
cwallenpoole
  • 79,954
  • 26
  • 128
  • 166
  • Just saying, this will not produce valid CSV as per RFC. – hakre Jul 06 '11 at 20:01
  • @hakre You're right, the proper line terminator is \r\n, which means the above will output valid CSV in Windows, but not *nix. – cwallenpoole Jul 06 '11 at 20:11
  • Uhm, imagine you have a value that contains a colon `,` or a linebreak or a quote `"` or even a mixture of such. I think the `PHP_EOL` is a minor issue compared to that. – hakre Jul 06 '11 at 20:13
  • Thought about that, but that does not fit with the OP's desired result. If he wanted, though, he could replace `$fin .= implode(',',$arr).PHP_EOL;` with `$fin .= '"' . implode('","',$arr)."\"\r\n";` – cwallenpoole Jul 06 '11 at 20:18
  • That said, use of PHP_EOL is incorrect for an actual CSV (instead of CSV-like), while failure to double quote is only ill-advised. – cwallenpoole Jul 06 '11 at 20:20
  • I ended up removing the PHP_EOL statements and using the "\r\n" as suggested above. – julio Jul 18 '11 at 17:23
0

I made a little class for doing this: https://gist.github.com/981720

You can include it and generate the csv like so:

CSV::export($field_names, $data, '', TRUE)

timw4mail
  • 1,716
  • 2
  • 13
  • 16
0

This is merely the same as Marc B suggested, however you wrote that you didn't wanted to create a file for this. Stream Wrappers to the rescue (not tested, just going off the top of my head):

$fh = fopen('php://memory', 'rw'); # don't create a file, write to memory instead

// write out the headers
fputcsv($fh, array_keys($data));

// write out the data    
for ($i = 0; $i < count($data['dates']); $i++) {
    $temp = array($data['dates'][$i], $data['type1'][$i], $data['type2'][$i]);
    fputcsv($fh, $temp);
}
rewind($fh);
$string = stream_get_contents($fh);
fclose($fh);
hakre
  • 193,403
  • 52
  • 435
  • 836
0

The task is to create a .csv-ready string. In other words, a comma-delimited line of header data, followed by n number of transposed column data joined as comma-delimited lines.

To transpose a multidimensional array, call array_map() and feed it an unpacked (...) indexed array then collect columns of data inside the callback (...$col) and implode.

After all data is merged into a single array of strings, implode the first level with \n or PHP_EOL.

Code: (Demo)

echo implode(
         "\n",
         array_merge(
             [implode(',', array_keys($data))],
             array_map(
                 fn(...$col) => implode(',', $col),
                 ...array_values($data)
             )
         )
     );

Output:

dates,type1,type2
2010-01-02,data1,data4
2011-02-03,data2,data5
2011-02-04,data3,data6

This is a reliable dynamic snippet for the desired result and at no point hard codes any key names.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136