Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Process CSV Into Array With Column Headings For Key

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.

like image 848
Bit Bucket Avatar asked Apr 16 '12 20:04

Bit Bucket


People also ask

Can CSV have headers?

CSV and spreadsheet content rules. Each row in the file must contain the same number of cells. This rule also applies to the header row. The first row must contain column headers.

How do I create an array in CSV?

To parse a CSV string into an array, you need to write a code that will separate the string between CSV headers and CSV rows. Then, you need to put each row as one object element, using the headers as the property names and the row as the values.

How do I display an array in CSV?

To convert an array into a CSV file we can use fputcsv() function. The fputcsv() function is used to format a line as CSV (comma separated values) file and writes it to an open file.

What is header line in CSV?

A header of the CSV file is an array of values assigned to each of the columns. It acts as a row header for the data. Initially, the CSV file is converted to a data frame and then a header is added to the data frame. The contents of the data frame are again stored back into the CSV file.


2 Answers

$all_rows = array(); $header = fgetcsv($file); while ($row = fgetcsv($file)) {   $all_rows[] = array_combine($header, $row); } 
print_r($all_rows); 
like image 89
Tim Cooper Avatar answered Sep 30 '22 00:09

Tim Cooper


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.

like image 29
hakre Avatar answered Sep 30 '22 01:09

hakre