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.

issue: I have an associative array, where all the keys represent the csv headers and the values in each $key => array.. represent the items in that column.

Research: To my knowledge, fputcsv likes to go in row by row, but this column-based array is making that complicated. I have not found any function out there that accomplishes this.

Example:

Array(
    ['fruits'] => Array(
        [0] => 'apples',
        [1] => 'oranges',
        [2] => 'bananas'
    ),
    ['meats'] => Array(
        [0] => 'porkchop',
        [1] => 'chicken',
        [2] => 'salami',
        [3] => 'rabbit'
    ),
)

Needs to become:

fruits,meats
apples,porkchop
oranges,chicken
bananas,salami
,rabbit

Why it's hard:

You need to know the max number of rows to make blank spots.

share|improve this question

2 Answers 2

up vote 1 down vote accepted

I had to write my own function. Thought maybe it might help someone else!

/*
 * The array is associative, where the keys are headers
 * and the values are the items in that column.
 * 
 * Because the array is by column, this function is probably costly.
 * Consider a different layout for your array and use a better function.
 * 
 * @param $array array The array to convert to csv.
 * @param $file string of the path to write the file.
 * @param $delimeter string a character to act as glue.
 * @param $enclosure string a character to wrap around text that contains the delimeter
 * @param $escape string a character to escape the enclosure character.
 * @return mixed int|boolean result of file_put_contents.
 */

function array_to_csv($array, $file, $delimeter = ',', $enclosure = '"', $escape = '\\'){
    $max_rows = get_max_array_values($array);
    $row_array = array();
    $content = '';
    foreach ($array as $header => $values) {
    $row_array[0][] = $header;
    $count = count($values);
    for ($c = 1; $c <= $count; $c++){
        $value = $values[$c - 1];
        $value = preg_replace('#"#', $escape.'"', $value);
        $put_value = (preg_match("#$delimeter#", $value)) ? $enclosure.$value.$enclosure : $value;
        $row_array[$c][] = $put_value;
    }
    // catch extra rows that need to be blank
    for (; $c <= $max_rows; $c++) {
        $row_array[$c][] = '';
    }
    }
    foreach ($row_array as $cur_row) {
    $content .= implode($delimeter,$cur_row)."\n";
    }
    return file_put_contents($file, $content);
}

And this:

/*
 * Get maximum number of values in the entire array.
 */
function get_max_array_values($array){
    $max_rows = 0;
    foreach ($array as $cur_array) {
    $cur_count = count($cur_array);
    $max_rows = ($max_rows < $cur_count) ? $cur_count : $max_rows;
    }
    return $max_rows;
}
share|improve this answer
    
Work Perfectly. Accepte your answer though. –  Adam Sinclair Sep 24 '14 at 23:46
    
I am waiting before accepting my own answer - because someone could have a better one :D –  amurrell Sep 25 '14 at 0:25
$data = array(
    'fruits' => array(
        'apples',
        'oranges',
        'bananas'
    ),
    'meats' => array(
        'porkchop',
        'chicken',
        'salami',
        'rabbit'
    ),
);

$combined = array(array('fruits', 'meats'));

for($i = 0; $i < max(count($data['fruits']), count($data['meats'])); $i++)
{   
    $row = array(); 

    $row[] = isset($data['fruits'][$i]) ? $data['fruits'][$i] : '';
    $row[] = isset($data['meats'][$i])  ? $data['meats'][$i]  : '';

    $combined[] = $row;
}

ob_start();

$fp = fopen('php://output', 'w');

foreach($combined as $row)
  fputcsv($fp, $row);

fclose($fp);

$data = ob_get_clean();

var_dump($data);

Conversion to csv and parsing of the array can be done in the same loop. Or did you mean that there might be more columns? This code can be easily modified for the general type of the array. Like this, for any number of the columns

$data = array(
    'fruits' => array(
        'apples',
        'oranges',
        'bananas'
    ),
    'meats' => array(
        'porkchop',
        'chicken',
        'salami',
        'rabbit'
    ),
);
$heads = array_keys($data);
$maxs = array();
foreach($heads as $head)
  $maxs[] = count($data[$head]);
ob_start();
$fp = fopen('php://output', 'w');
fputcsv($fp, $heads);
for($i = 0; $i < max($maxs); $i++)
{   
    $row = array(); 
    foreach($heads as $head)
       $row[] = isset($data[$head][$i]) ? $data[$head][$i] : '';
    fputcsv($fp, $row);   
}
fclose($fp);
$data = ob_get_clean();
var_dump($data);
share|improve this answer
    
Now the question is, which one of these solutions is less costly/more efficient - especially with huge arrays (lots of columns). –  amurrell Sep 25 '14 at 0:25
    
@amurrell my code should work with huge arrays without the problem, intermediate data is not stored anywhere, no regular expressions. one thing that can be done to improve is to move max() from the loop into separate variable. it just reads row by row, replacing missing items with empty string, and converts each row into csv line, nothing else. output buffering is only in order to store csv string into variable, you can save directly to file without it. –  Cheery Sep 25 '14 at 0:27

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.