Process CSV Into Array With Column Headings For Key

岁酱吖の 提交于 2019-11-26 09:39:38

问题


I have a CSV with the first row containing the field names. Example data is...

\"Make\",\"Model\",\"Note\"
\"Chevy\",\"1500\",\"loaded\"
\"Chevy\",\"2500\",\"\"
\"Chevy\",\"\",\"loaded\"

I need my data formatted in an array of key-value pairs where the key name is the column heading. I guess it would something like this for row 1:

$array = [
    \"Make\" => \"Chevy\",
    \"Model\" => \"1500\",
    \"Note\" => \"loaded\"
];

...row 2...

$array = [
    \"Make\" => \"Chevy\",
    \"Model\" => \"1500\",
    \"Note\" => \"\"
];

...and row 3...

$array = [
    \"Make\" => \"Chevy\",
    \"Model\" => \"\",
    \"Note\" => \"loaded\"
];

I\'m not sure how to do this other than statically - problem is the columns with their associated data could change from one file to the next... columns rearranged, dropped, or added.

You ideas are much appreciated.


回答1:


$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
  $all_rows[] = array_combine($header, $row);
}
print_r($all_rows);



回答2:


PHP offers already 99,9% of what you need within SplFileObject, you add the missing 0,1% by extending from it. In the following example CSVFile extends from it:

$csv = new CSVFile('../data/test.csv');

foreach ($csv as $line)
{
    var_dump($line);
}

With your example data:

array(3) {
  ["Make"]=>  string(5) "Chevy"
  ["Model"]=> string(4) "1500"
  ["Note"]=>  string(6) "loaded"
}
array(3) {
  ["Make"]=>  string(5) "Chevy"
  ["Model"]=> string(4) "2500"
  ["Note"]=> string(0) ""
}
array(3) {
  ["Make"]=>  string(5) "Chevy"
  ["Model"]=> string(0) ""
  ["Note"]=>  string(6) "loaded"
}

CSVFile is defined as the following:

class CSVFile extends SplFileObject
{
    private $keys;

    public function __construct($file)
    {
        parent::__construct($file);
        $this->setFlags(SplFileObject::READ_CSV);
    }

    public function rewind()
    {
        parent::rewind();
        $this->keys = parent::current();
        parent::next();
    }

    public function current()
    {
        return array_combine($this->keys, parent::current());
    }

    public function getKeys()
    {
        return $this->keys;
    }
}

If you do it this way, the details are nicely encapsulated away. Additionally it's more easy to deal with errors (e.g. count mismatch) inside the current() function so the code which makes use of the data does not need to deal with it.

Edit:

However the example given is short in terms of re-usablity. Instead of extending from SplFileObject it's much better to aggregate it:

class KeyedArrayIterator extends IteratorIterator
{
    private $keys;

    public function rewind()
    {
        parent::rewind();
        $this->keys = parent::current();
        parent::next();
    }

    public function current()
    {
        return array_combine($this->keys, parent::current());
    }

    public function getKeys()
    {
        return $this->keys;
    }
}

The code is identical but the details that were encapsulated in the constructor are left out. This reduction allows to use the type more broadly, e.g. with (but not only with) the said SplFileObject:

$file = new SplFileObject('../data/test.csv');
$file->setFlags($file::READ_CSV);

$csv = new KeyedArrayIterator($file);

foreach ($csv as $line) {
    var_dump($line);
}

If that now sounds too verbose, it again can be wrapped to give it again a nicer facade:

class CSVFile extends KeyedArrayIterator
{
    /**
     * @param string $file
     */
    public function __construct($file)
    {
        parent::__construct(new SplFileObject($file));
        $this->setFlags(SplFileObject::READ_CSV);
    }
}

Thanks to the standard decorating-ability of TraversableIterator, the original constructor code from the first example of CSVFile could just be copied 100%.

This last addition also allows to keep the original code that uses the CSVFile Iterator intact:

$csv = new CSVFile('../data/test.csv');

foreach ($csv as $line) {
    var_dump($line);
}

So just a quick refactoring to allow more code-reuse. You get a KeyedArrayIterator for free.




回答3:


$csv_data = array_map('str_getcsv', file('Book.csv'));// reads the csv file in php array
$csv_header = $csv_data[0];//creates a copy of csv header array
unset($csv_data[0]);//removes the header from $csv_data since no longer needed
foreach($csv_data as $row){
    $row = array_combine($csv_header, $row);// adds header to each row as key
    var_dump($row);//do something here with each row
}



回答4:


function processCsv($absolutePath)
{
    $csv = array_map('str_getcsv', file($absolutePath));
    $headers = $csv[0];
    unset($csv[0]);
    $rowsWithKeys = [];
    foreach ($csv as $row) {
        $newRow = [];
        foreach ($headers as $k => $key) {
            $newRow[$key] = $row[$k];
        }
        $rowsWithKeys[] = $newRow;
    }
    return $rowsWithKeys;
}



回答5:


At this point I'm assuming you've already solved the issue but thought I'd throw in a suggested way around this, probably not the best/most elegant solution but it does the trick:

$row = 1;
$array = array();
$marray = array();
$handle = fopen('file.csv', 'r');
if ($handle !== FALSE) {
    while (($data = fgetcsv($handle, 0, ',')) !== FALSE) {
        if ($row === 1) {
            $num = count($data);
            for ($i = 0; $i < $num; $i++) {
                array_push($array, $data[$i]);
            }
        }
        else {
            $c = 0;
            foreach ($array as $key) {
                $marray[$row - 1][$key] = $data[$c];
                $c++;
            }
        }
        $row++;
    }
    echo '<pre>';
    print_r($marray);
    echo '</pre>';
}



回答6:


Try with this code:

$query = "SELECT * FROM datashep_AMS.COMPLETE_APPLICATIONS";
$export= mysql_query($query);
$first = true;
$temp = $export[0];
//echo "<pre>"; print_r($first); exit;

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

$outstream = fopen("php://output", "w");



foreach($export as $result)
{
    if($first){
        $titles = array();
        foreach($temp as $key=>$val){
            $titles[] = $key;
        }
        //print_r ($titles);exit;
        fputcsv($outstream, $titles);
    }
    $first = false;
    fputcsv($outstream, $result);
}

fclose($outstream);

Thanks




回答7:


Try this

$csv = array_map("str_getcsv", file('file.csv', FILE_SKIP_EMPTY_LINES));    
$header = array_shift($csv); // get header from array

foreach ($csv as $key => $value) {    
    $csv[$key] = array_combine($header, $value);
    var_dump($csv[$key]['Model']);
}

var_dump($csv);



回答8:


In the answer of Tim Cooper above, instead of

$all_rows = array();
$header = null;
while ($row = fgetcsv($file)) {
    if ($header === null) {
        $header = $row;
        continue;
    }
    $all_rows[] = array_combine($header, $row);
}

I would code, in a more elegant and efficient way:

$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
    $all_rows[] = array_combine($header, $row);
}



回答9:


The array_combine() function only works if header colums match the data colums otherwise an error is thrown.



来源:https://stackoverflow.com/questions/10181054/process-csv-into-array-with-column-headings-for-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!