Массив в CSV по столбцам

вопрос: У меня есть ассоциативный массив, где все ключи представляют заголовки CSV, а значения в каждом массиве $ key => .. представляют элементы в этом столбце.

Исследование: Насколько мне известно, fputcsv любит ходить по строкам, но этот массив на основе столбцов усложняет задачу. Я не нашел никакой функции, которая бы выполняла это.

Пример:

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

Должен стать:

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

Почему это сложно

Вам нужно знать максимальное количество строк, чтобы сделать пустые места.

1

Решение

Я должен был написать свою собственную функцию. Подумал, может быть, это поможет кому-то еще!

/*
* 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);
}

И это:

/*
* 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;
}

Некоторое время спустя я написал для этого класс, который я предоставлю любому, кто сейчас его ищет:

class CSVService {

protected $csvSyntax;

public function __construct()
{
return $this;
}

public function renderCSV($contents, $filename = 'data.csv')
{
header('Content-type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '"');

echo $contents;
}

public function CSVtoArray($filename = '', $delimiter = ',') {
if (!file_exists($filename) || !is_readable($filename)) {
return false;
}

$headers = null;
$data = array();
if (($handle = fopen($filename, 'r')) !== false) {
while (($row = fgetcsv($handle, 0, $delimiter, '"')) !== false) {
if (!$headers) {
$headers = $row;
array_walk($headers, 'trim');
$headers = array_unique($headers);
} else {
for ($i = 0, $j = count($headers); $i < $j;  ++$i) {
$row[$i] = trim($row[$i]);
if (empty($row[$i]) && !isset($data[trim($headers[$i])])) {
$data[trim($headers[$i])] = array();
} else if (empty($row[$i])) {
continue;
} else {
$data[trim($headers[$i])][] = stripcslashes($row[$i]);
}
}
}
}
fclose($handle);
}
return $data;
}

protected function getMaxArrayValues($array)
{
return array_reduce($array, function($carry, $item){
return ($carry > $c = count($item)) ? $carry : $c;
}, 0);
}

private function getCSVHeaders($array)
{
return array_reduce(
array_keys($array),
function($carry, $item) {
return $carry . $this->prepareCSVValue($item) . $this->csvSyntax->delimiter;
}, '') . "\n";
}

private function prepareCSVValue($value, $delimiter = ',', $enclosure = '"', $escape = '\\')
{
$valueEscaped = preg_replace('#"#', $escape . '"', $value);
return (preg_match("#$delimiter#", $valueEscaped)) ?
$enclosure . $valueEscaped . $enclosure : $valueEscaped;
}

private function setUpCSVSyntax($delimiter, $enclosure, $escape)
{
$this->csvSyntax = (object) [
'delimiter' => $delimiter,
'enclosure' => $enclosure,
'escape'    => $escape,
];
}

private function getCSVRows($array)
{
$n = $this->getMaxArrayValues($array);
$even = array_values(
array_map(function($columnArray) use ($n) {
for ($i = count($columnArray); $i <= $n; $i++) {
$columnArray[] = '';
}
return $columnArray;
}, $array)
);

$rowString = '';

for ($row = 0; $row < $n; $row++) {
for ($col = 0; $col < count($even); $col++) {
$value = $even[$col][$row];
$rowString .=
$this->prepareCSVValue($value) .
$this->csvSyntax->delimiter;
}
$rowString .= "\n";
}

return $rowString;
}

public function arrayToCSV($array, $delimiter = ',', $enclosure = '"', $escape = '\\', $headers = true) {
$this->setUpCSVSyntax($delimiter, $enclosure, $escape);

$headersString = ($headers) ? $this->getCSVHeaders($array) : '';

$rowsString = $this->getCSVRows($array);return $headersString . $rowsString;
}

}
3

Другие решения

$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);

Преобразование в csv и разбор массива могут быть выполнены в одном цикле. Или вы имели в виду, что может быть больше столбцов? Этот код может быть легко изменен для общего типа массива. Вот так, для любого количества столбцов

$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);
0

По вопросам рекламы [email protected]