LeagueCSV

Versions

Result Set

A League\Csv\ResultSet object represents the associated result set of processing a CSV document with a constraint builder. This object is returned from Statement::process execution.

Starting with version 9.6.0, the class implements the League\Csv\TabularDataReader interface.

Starting with version 9.8.0, the class implements the ::fetchColumnByName and ::fetchColumnByOffset methods.

Information

Accessing the result set column names

public ResultSet::getHeader(): array

ResultSet::getHeader returns the header associated with the current object.

Example: no header information was given

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$records = Statement::create()->process($reader);
$records->getHeader(); //is empty because no header information was given

Example: header information given by the Reader object

use League\Csv\Reader;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);

$records = Statement::create()->process($reader);
$records->getHeader(); //returns ['First Name', 'Last Name', 'E-mail'];

Example: header information given by the Statement object

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);

$records = Statement::create()->process($reader, ['Prénom', 'Nom', 'E-mail']);
$records->getHeader(); //returns ['Prénom', 'Nom', 'E-mail'];

Accessing the number of records in the result set

The ResultSet class implements the Countable interface.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$records = Statement::create()->process($reader);
count($records); //return the total number of records found

Records

Description

public ResultSet::getRecords(array $header = []): Iterator

Starting with version 9.6.0, the implemented ResultSet::getRecords method matches the same arguments and the same signature as the Reader::getRecords method.

To iterate over each found record you can call the ResultSet::getRecords method which returns a Generator of all records found or directly use the foreach construct as the class implements the IteratorAggregate interface:

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$records = Statement::create()->process($reader);

foreach ($records->getRecords() as $record) {
    //do something here
}

foreach ($records as $record) {
    //do something here
}

Usage with the header

If the ResultSet::getHeader is not an empty array the found records keys will contain the returned values.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);
$records = Statement::create()->process($reader);
$records->getHeader(); //returns ['First Name', 'Last Name', 'E-mail']
foreach ($records as $record) {
    // $record contains the following data
    // array(
    //     'First Name' => 'john',
    //     'Last Name' => 'doe',
    //     'E-mail' => 'john.doe@example.com',
    // );
}

Selecting a specific record

Since version 9.9.0, the class implements the ::first and ::nth methods. These methods replace the ::fetchOne method which is deprecated and will be removed in the next major release.

These methods all return a single record from the ResultSet.

public ResultSet::fetchOne(int $nth_record = 0): array
public ResultSet::first(): array
public ResultSet::nth(int $nth_record): array

The $nth_record argument represents the nth record contained in the result set starting at 0.
In the case of fetchOne, if no argument is given the method will return the first record from the result set.

In all cases, if no record is found, an empty array is returned.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);

$stmt = Statement::create()
    ->offset(10)
    ->limit(12)
;
$result = $stmt->process($reader);

$result->fetchOne(3);
$result->nth(3);
// access the 4th record from the recordset (indexing starts at 0)
// will return something like this :
//
//   ['john', 'doe', 'john.doe@example.com']
//

$result->fetchOne();
$result->first();
$result->nth(0);
//returns the first matching record from the recordset or an empty record if none is found.

nth will throw an ArgumentCountError if no argument is given to it.

Selecting a single column

public ResultSet::fetchColumnByName(string $name): Iterator
public ResultSet::fetchColumnByOffset(int $offset = 0): Iterator
public ResultSet::fetchColumn(string|int $columnIndex = 0): Iterator

Since version 9.8.0, the class implements the ::fetchColumnByName and ::fetchColumnByOffset methods. These methods replace the ::fetchColumn method which is deprecated and will be removed in the next major release.

Both methods return an Iterator of all values in a given column from the ResultSet object, but they differ in their argument type:

::fetchColumnByName expects a string representing one of the values of ResultSet::getHeader

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);
$records = Statement::create()->process($reader);
foreach ($records->fetchColumnByName('E-mail') as $value) {
    //$value is a string representing the value
    //of a given record for the selected column
    //$value may be equal to 'john.doe@example.com'
}

If the ResultSet contains column names and the $name is not found, an Exception exception is thrown.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);

$records = Statement::create()->process($reader);
foreach ($records->fetchColumnByName('foobar') as $record) {
    //throw an Exception exception if
    //no `foobar` column name is found
    //in $records->getHeader() result
}

::fetchColumnByOffset expects an integer representing the column index starting from 0;

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$records = Statement::create()->process($reader);
foreach ($records->fetchColumnByOffset(2) as $value) {
    //$value is a string representing the value
    //of a given record for the selected column
    //$value may be equal to 'john.doe@example.com'
}

For both methods, if for a given record the column value is null, the record will be skipped.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$records = Statement::create()->process($reader);
count($records); //returns 10;
count(iterator_to_array($records->fetchColumnByOffset(2), false)); //returns 5
//5 records were skipped because the column value is null

The following paragraph describes the usage of the ::fetchColumn method which is deprecated as of 9.8.0 and wil be removed in the next major release.

ResultSet::fetchColumn returns a Generator of all values in a given column from the ResultSet object.

The $columnIndex parameter can be:

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$records = Statement::create()->process($reader);
foreach ($records->fetchColumn(2) as $value) {
    //$value is a string representing the value
    //of a given record for the selected column
    //$value may be equal to 'john.doe@example.com'
}

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);

$records = Statement::create()->process($reader);
foreach ($records->fetchColumn('E-mail') as $value) {
    //$value is a string representing the value
    //of a given record for the selected column
    //$value may be equal to 'john.doe@example.com'
}
use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$records = Statement::create()->process($reader);
count($records); //returns 10;
count(iterator_to_array($records->fetchColumn(2), false)); //returns 5
//5 records were skipped because the column value is null

If the ResultSet contains column names and the $columnIndex is not found, an Exception exception is thrown.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$reader->setHeaderOffset(0);

$records = Statement::create()->process($reader);
foreach ($records->fetchColumn('foobar') as $record) {
    //throw an Exception exception if
    //no `foobar` column name is found
    //in $records->getHeader() result
}

Selecting key-value pairs

ResultSet::fetchPairs method returns a Generator of key-value pairs.

public ResultSet::fetchPairs(string|int $offsetIndex = 0, string|int $valueIndex = 1): Generator

Both arguments, $offsetIndex and $valueIndex can be:

These arguments behave exactly like the $columnIndex from ResultSet::fetchColumn.

use League\Csv\Reader;
use League\Csv\Statement;

$str = <<<EOF
john,doe
jane,doe
foo,bar
sacha
EOF;

$reader = Reader::createFromString($str);
$records = Statement::create()->process($reader);

foreach ($records->fetchPairs() as $firstname => $lastname) {
    // - first iteration
    // $firstname -> 'john'
    // $lastname  -> 'doe'
    // - second iteration
    // $firstname -> 'jane'
    // $lastname  -> 'doe'
    // - third iteration
    // $firstname -> 'foo'
    // $lastname  -> 'bar'
    // - fourth iteration
    // $firstname -> 'sacha'
    // $lastname  -> null
}

Notes

If the ResultSet contains column names and the submitted arguments are not found, an Exception exception is thrown.

Collection methods

New methods added in version 9.11.

To ease working with the ResultSet the following methods derived from collection are added. Some are just wrapper methods around the Statement class while others use the iterable nature of the instance.

ResultSet::each

Iterates over the records in the CSV document and passes each item to a closure:

use League\Csv\Reader;
use League\Csv\Statement;
use League\Csv\Writer;

$writer = Writer::createFromString('');
$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');

$resultSet = Statement::create()->process($reader);
$resultSet->each(function (array $record, int $offset) use ($writer) {
     if ($offset < 10) {
        return $writer->insertOne($record);
     }
     
     return false;
});

//$writer will contain at most 10 lines coming from the $resultSet.
// the iteration stopped when the closure return false.

ResultSet::exists

Tests for the existence of an element that satisfies the given predicate.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$resultSet = Statement::create()->process($reader);

$exists = $resultSet->exists(fn (array $records) => in_array('twenty-five', $records, true));

//$exists returns true if at cell one cell contains the word `twenty-five` otherwise returns false,

Reader::reduce

Applies iteratively the given function to each element in the collection, so as to reduce the collection to a single value.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$resultSet = Statement::create()->process($reader);

$nbTotalCells = $resultSet->recude(fn (?int $carry, array $records) => ($carry ?? 0) + count($records));

//$records contains the total number of celle contains in the $resultSet

Reader::filter

Returns all the elements of this collection for which your callback function returns true. The order and keys of the elements are preserved.

Wraps the functionality of Statement::where.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$resultSet = Statement::create()->process($reader);

$records = $resultSet->filter(fn (array $record): => 5 === count($record));

//$recors is a ResultSet object with only records with 5 elements

Reader::slice

Extracts a slice of $length elements starting at position $offset from the Collection. If $length is -1 it returns all elements from $offset to the end of the Collection. Keys have to be preserved by this method. Calling this method will only return the selected slice and NOT change the elements contained in the collection slice is called on.

Wraps the functionality of Statement::offset and Statement::limit.

use League\Csv\Reader;
use League\Csv\Statement;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$resultSet = Statement::create()->process($reader);

$records = $resultSet->slice(10, 25);

//$records contains up to 25 rows starting at the offset 10 (the eleventh rows)

Reader::sorted

Sorts the CSV document while keeping the original keys.

Wraps the functionality of Statement::orderBy.

use League\Csv\Reader;

$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
$resultSet = Statement::create()->process($reader);

$records = $resultSet->sorted(fn (array $recordA, array $recordB) => $recordA['firstname'] <=> $recordB['firstname']);

//$records is a ResultSet containing the original resultSet. 
//The original ResultSet is not changed

Conversions

Json serialization

The ResultSet class implements the JsonSerializable interface. As such you can use the json_encode function directly on the instantiated object. The interface is implemented using PHP’s iterator_array on the ResultSet::getRecords method. As such, the returned JSON string data is affected by the presence or absence of column names.

use League\Csv\Reader;
use League\Csv\Statement;

$records = [
    ['firstname', 'lastname', 'e-mail', 'phone'],
    ['john', 'doe', 'john.doe@example.com', '0123456789'],
    ['jane', 'doe', 'jane.doe@example.com', '0123456789'],
];

$tmp = new SplTempFileObject();
foreach ($records as $record) {
    $tmp->fputcsv($record);
}

$reader = Reader::createFromFileObject($tmp)->setHeaderOffset(0);
$stmt = Statement::create()->offset(1)->limit(1);
$result = $stmt->process($reader);

echo '<pre>', PHP_EOL;
echo json_encode($result, JSON_PRETTY_PRINT), PHP_EOL;
//display
//[
//    {
//        "firstname": "jane",
//        "lastname": "doe",
//        "e-mail": "jane.doe@example.com",
//        "phone": "0123456789"
//    }
//]

The record offset is not preserved on conversion

To convert your CSV records to JSON you must be sure its content is UTF-8 encoded, using, for instance, the library CharsetConverter stream filter.

Other conversions

If you wish to convert your CSV document in XML or HTML please refer to the converters bundled with this library.