Создание CSV с динамическими сгруппированными заголовками в PHP из массива

#php #arrays #csv #multidimensional-array

#php #массивы #csv #многомерный-массив

Вопрос:

Не мог бы кто-нибудь помочь в выполнении следующих задач, пожалуйста? Как создать экспорт CSV из многомерного массива, но иметь динамические сгруппированные заголовки столбцов

 Array ( 
    [0] => Array ( [months] => 06/2020 [hours] => 202 [skill] => 5 ) 
    [1] => Array ( [months] => 06/2020 [hours] => 563.5 [skill] => 6 ) 
    [2] => Array ( [months] => 07/2020 [hours] => 140.5 [skill] => 6 ) 
    [3] => Array ( [months] => 07/2020 [hours] => 522.5 [skill] => 5 ) 
)
  

Таким образом, вывод в CSV будет выглядеть так

  ---------------------------- ------------ -------- 
|                            | Skill 6    |Skill 5 |
 ---------------------------- ------------ -------- 
| 06/2020                    | 563.5      | 202    |
 ---------------------------- ------------ -------- 
| 07/2020                    | 140.5      | 522.5  |
 ---------------------------- ------------ -------- 
  

Добавлен вывод CSV, который у меня есть до сих пор
Текущий CSV-элемент кода

 header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=result_file.csv");
header("Pragma: no-cache");
header("Expires: 0");

// Building $data_array from DB
foreach ($data_array as $subarray) {
    $tempKey = $subarray['skill'].$subarray['months'];  
    $subarray['hours'] = str_replace(',', '', $subarray['hours']); 
    if (isset($result[$tempKey])) {
        $result[$tempKey]['hours']  = $subarray['hours'];
    } else {
        $result[$tempKey] = $subarray;
    }
}

// CSV Output
outputCSV($result);

function outputCSV($result) {
    $output = fopen("php://output", "w");
    foreach ($result as $row) {
        fputcsv($output, $row);
    }
    fclose($output);
}
  

любая помощь будет с благодарностью принята, TIA

Отредактированный вопрос

Комментарии:

1. Я это заслужил! ха-ха Следующий вопрос: Не могли бы вы поделиться со мной своими знаниями по этому вопросу

2. Нет, я не против поделиться

Ответ №1:

Уверен, что если бы я немного подумал об этом, я мог бы улучшить его, но, похоже, он получает правильный ответ

 $in = [
    [ 'months' => '06/2020', 'hours' => 202, 'skill' => 5  ],
    [ 'months' => '06/2020', 'hours' => 563.5, 'skill' => 6 ], 
    [ 'months' => '07/2020', 'hours' => 140.5, 'skill' => 6 ], 
    [ 'months' => '07/2020', 'hours' => 522.5, 'skill' => 5 ]
];

$firstTitle = 'Month';
$months = [];
$skills = [$firstTitle=>1];

// make an array keyed on the date
foreach ( $in as $t) {
    $months[$t['months']]['skill'.$t['skill']] = $t['hours'];
    $skills['skill'.$t['skill']] = 1;
}

// sort skills into assending order
ksort($skills);

// open a file
$xl = fopen('excelfile.csv', 'w');

// echo title line from the skills array
fputcsv($xl, array_keys($skills));

// build csv line with skills in the correct order
foreach ($months as $date => $m){
    // build array in correct sorted order
    $t = [];
    $t[] = $date;
    foreach ($skills as $skill => $x) {
        if ( $skill != $firstTitle) $t[] = $m[$skill];
    }
    
    fputcsv($xl,$t);  
}
  

Результат

 Month,skill5,skill6
06/2020,202,563.5
07/2020,522.5,140.5
  

Ответ №2:

По сути, вы просто агрегируете значения навыков по месяцам, а затем выводите эти агрегированные значения. Это не сложно, но вы должны четко понимать, что вы делаете. Одна из распространенных причин, по которой я вижу, что новые игроки путаются, заключается в том, что они пытаются использовать максимально компактный код, что затрудняет отслеживание происходящего. Будьте подробны, четко называйте вещи и неустанно комментируйте свой код. Вам будет намного легче понять, почему что-то не работает, и ваш код будет намного удобнее в обслуживании. Напишите свой код так, как будто его будет поддерживать кто-то другой. Что кто-то другой может быть вами через пять лет.

 <?php
$dataArray = [
    ['months' => '06/2020', 'hours' => '202', 'skill' => '5'],
    ['months' => '06/2020', 'hours' => '563.5', 'skill' => '6'],
    ['months' => '06/2020', 'hours' => '303.7', 'skill' => '6'],
    ['months' => '08/2020', 'hours' => '123.5', 'skill' => '8'],
    ['months' => '07/2020', 'hours' => '140.5', 'skill' => '6'],
    ['months' => '07/2020', 'hours' => '522.5', 'skill' => '5'],
    ['months' => '08/2020', 'hours' => '123.5', 'skill' => '6']
];

/*
 * Break out your formatting into functions so that it's re-usable and doesn't clutter up your logic
 */
function formatHours($hourString)
{
    $hourString = str_replace(',', '', $hourString);
    return floatval($hourString);
}

function buildSkillKey($skillValue)
{
    return 'Skill '.$skillValue;
}

// Set up buffers for our skills and month values
$skills = [];
$buffer = [];
foreach($dataArray as $currRow)
{
    //Format the hour value
    $currHours = formatHours($currRow['hours']);

    //Create key for the skill.
    $skillKey = buildSkillKey($currRow['skill']);

    /*
     * Add the skill to the skill buffer. Using the value as the key is an easy way to both prevent duplicates
     * without having to implement any logic, and have automatic alpha sorting
     */
    $skills[$skillKey] = $skillKey;

    // Set up an array for the month value if we don't have one already
    if(!array_key_exists($currRow['months'], $buffer))
    {
        $buffer[$currRow['months']] = [];
    }

    /*
     * If you don't have multiple month/skill entries that you need to aggregate, remove this condition
     * and simply set the value in the buffer rather than adding with  =
     */
    if(!array_key_exists($skillKey, $buffer[$currRow['months']]))
    {
        $buffer[$currRow['months']][$skillKey] = 0;
    }

    $buffer[$currRow['months']][$skillKey]  = $currHours;
}

// Define a string for the months column header
$monthColumnTitle = '';

// Create the header row by combining the month header and the skills buffer
$header = array_merge([$monthColumnTitle], $skills);

// Open an output handle and send the header
$outputHandle = fopen("skills.csv", "w");
fputcsv($outputHandle, $header);

// Spin through the buffer
foreach($buffer  as $currMonth=>$currSkillValues)
{
    // Initialize an output array with the month in the first position
    $currOutput = [$currMonth];

    // Iterate through the skill buffer
    foreach($skills as $currSkillLabel)
    {
        /*
         * If we have a value for this skill, add it to the output row, otherwise insert an empty string.
         *
         * If you prefer to send zeros rather than empty strings, you can just set the field value to
         * $currSkillValues[$currSkillLabel], since we initialized all skills with zeroes when building
         * the value buffer.
         */
        $currFieldValue = (!empty($currSkillValues[$currSkillLabel])) ? $currSkillValues[$currSkillLabel]:'';
        $currOutput[] = $currFieldValue;
    }

    // Send the row
    fputcsv($outputHandle, $currOutput);
}
  

Комментарии:

1. Спасибо, я также попробовал это решение, которое также работает! 🙂