diff --git a/docs/9.0/reader/index.md b/docs/9.0/reader/index.md
index 8ca0536c..b87e41b3 100644
--- a/docs/9.0/reader/index.md
+++ b/docs/9.0/reader/index.md
@@ -5,18 +5,17 @@ title: CSV document Reader connection
# Reader Connection
-The `League\Csv\Reader` class extends the general connections [capabilities](/9.0/connections/) to ease selecting and manipulating CSV document records.
+The `League\Csv\Reader` class extends the general connections [capabilities](/9.0/connections/) to ease selecting
+and manipulating CSV document records.
Starting with version 9.1.0
, createFromPath
will have its default set to r
.
Prior to 9.1.0
, by default, the mode for a Reader::createFromPath
is r+
which looks for write permissions on the file and throws an Exception
if the file cannot be opened with the permission set. For sake of clarity, it is strongly suggested to set r
mode on the file to ensure it can be opened.
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.
-Starting with version 9.9.0
, the class implements the ::first
and ::nth
methods.
-Starting with version 9.11.0
, the class implements the collections methods.
## CSV example
-Many examples in this reference require a CSV file. We will use the following file `file.csv` containing the following data:
+Many examples in this reference require a CSV file. We will use the following file `file.csv`
+containing the following data:
```csv
"First Name","Last Name",E-mail
@@ -30,6 +29,8 @@ jane,jane
You can set and retrieve the header offset as well as its corresponding record.
+getHeader
is part of the TabularDataReader
.
+
### Description
```php
@@ -55,8 +56,9 @@ If no header offset is set:
- `Reader::getHeaderOffset` will return `null`.
By default no header offset is set.
-
-Because the header is lazy loaded, if you provide a positive offset for an invalid record a SyntaxError
exception will be triggered when trying to access the invalid record.
+Because the header is lazy loaded, if you provide a positive offset
+for an invalid record a SyntaxError
exception will be triggered when trying
+to access the invalid record.
```php
use League\Csv\Reader;
@@ -80,7 +82,8 @@ $header_offset = $csv->getHeaderOffset(); //returns 0
$header = $csv->getHeader(); //throws a SyntaxError exception
```
-Starting with 9.7.0
the SyntaxError
exception thrown will return the list of duplicate column names.
+Starting with 9.7.0
the SyntaxError
exception thrown
+will return the list of duplicate column names.
```php
use League\Csv\Reader;
@@ -103,10 +106,13 @@ try {
public Reader::getRecords(array $header = []): Iterator
```
+getRecords
is part of the TabularDataReader
.
+
### Reader::getRecords basic usage
The `Reader` class lets you access all its records using the `Reader::getRecords` method.
-The method returns an `Iterator` containing all CSV document records. It will extract the records using the [CSV controls characters](/9.0/connections/controls/);
+The method returns an `Iterator` containing all CSV document records. It will extract
+the records using the [CSV controls characters](/9.0/connections/controls/);
```php
use League\Csv\Reader;
@@ -126,7 +132,8 @@ foreach ($records as $offset => $record) {
### Reader::getRecords with Reader::setHeaderOffset
-If you specify the CSV header offset using `setHeaderOffset`, the found record will be combined to each CSV record to return an associative array whose keys are composed of the header values.
+If you specify the CSV header offset using `setHeaderOffset`, the found record will be combined to
+each CSV record to return an associative array whose keys are composed of the header values.
```php
use League\Csv\Reader;
@@ -147,7 +154,8 @@ foreach ($records as $offset => $record) {
### Reader::getRecords with its optional argument
-Conversely, you can submit your own header record using the optional `$header` argument of the `getRecords` method.
+Conversely, you can submit your own header record using the optional `$header` argument of
+the `getRecords` method.
```php
use League\Csv\Reader;
@@ -165,7 +173,9 @@ foreach ($records as $offset => $record) {
}
```
-The optional $header
argument from the Reader::getRecords
takes precedence over the header offset property but its corresponding record will still be removed from the returned Iterator
.
+The optional $header
argument from the Reader::getRecords
+takes precedence over the header offset property but its corresponding record will still be removed
+from the returned Iterator
.
```php
use League\Csv\Reader;
@@ -185,7 +195,8 @@ foreach ($records as $offset => $record) {
//the first record will still be skipped!!
```
-In both cases, if the header record contains non-unique string values, a Exception
exception is triggered.
+In both cases, if the header record contains non-unique string values,
+a Exception
exception is triggered.
since 9.12.0
the optional $header
is a full mapper
@@ -193,8 +204,8 @@ The argument now links the records column offset to a specific column name. In o
that the array key which MUST be a positive integer or `0` will correspond to the CSV column offset
and its value will represent its header value.
-This means that you can re-arrange the column order as well as removing or adding column to the returned iterator.
-Added column will only contain the `null` value.
+This means that you can re-arrange the column order as well as removing or adding column to the
+returned iterator. Added column will only contain the `null` value.
Here's an example of the new behaviour.
@@ -222,27 +233,6 @@ var_dump([...$records][0]);
As you can see the `Count` column is missing, the `Year` and `Firstname` columns are re-arranged but
present and the extra `Yolo` column is added with the value `null`
-### Using the IteratorAggregate interface
-
-Because the `Reader` class implements the `IteratorAggregate` interface you can directly iterate over each record using the `foreach` construct and an instantiated `Reader` object.
-You will get the same results as if you had called `Reader::getRecords` without its optional argument.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$reader->setHeaderOffset(0);
-foreach ($reader as $offset => $record) {
- //$offset : represents the record offset
- //var_export($record) returns something like
- // array(
- // 'First Name' => 'john',
- // 'Last Name' => 'doe',
- // 'E-mail' => john.doe@example.com'
- // );
-}
-```
-
## Records normalization
### General Rules
@@ -322,7 +312,8 @@ echo $reader->toString(); //returns the original $csv value without the formatti
New since version 9.4.0
-By default, the CSV document normalization removes empty records, but you can control the presence of such records using the following methods:
+By default, the CSV document normalization removes empty records, but you can control the presence of
+such records using the following methods:
```php
Reader::skipEmptyRecords(): self;
@@ -330,10 +321,13 @@ Reader::includeEmptyRecords(): self;
Reader::isEmptyRecordsIncluded(): bool;
```
-- Calling `Reader::includeEmptyRecords` will ensure empty records are left in the `Iterator` returned by `Reader::getRecords`,
-conversely `Reader::skipEmptyRecords` will ensure empty records are skipped.
-- At any given time you can ask your Reader instance if empty records will be stripped or included using the `Reader::isEmptyRecordsIncluded` method.
-- If no header offset is specified, the empty record will be represented by an empty `array`. Conversely, for consistency, an empty record will be represented by an array filled with `null` values as expected from header presence normalization.
+- Calling `Reader::includeEmptyRecords` will ensure empty records are left in the `Iterator` returned by
+`Reader::getRecords`, conversely `Reader::skipEmptyRecords` will ensure empty records are skipped.
+- At any given time you can ask your Reader instance if empty records will be stripped or
+included using the `Reader::isEmptyRecordsIncluded` method.
+- If no header offset is specified, the empty record will be represented by an empty `array`.
+Conversely, for consistency, an empty record will be represented by an array filled
+with `null` values as expected from header presence normalization.
The record offset is always independent of the presence of empty records.
@@ -381,205 +375,21 @@ $res = iterator_to_array($reader, true);
// ];
```
-## Records count
-
-You can retrieve the number of records contained in a CSV document using PHP's `count` function because the `Reader` class implements the `Countable` interface.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-count($reader); //returns 4
-```
-
-If a header offset is specified, the number of records will not take into account the header record.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$reader->setHeaderOffset(0);
-count($reader); //returns 3
-```
-
-New since version 9.4.0
-
-If empty records are to be preserved, the number of records will be affected.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file-with-two-empty-records.csv', 'r');
-$reader->isEmptyRecordsIncluded(); //returns false
-count($reader); //returns 2
-
-$reader->includeEmptyRecords();
-$reader->isEmptyRecordsIncluded(); //returns true
-count($reader); //returns 4
-```
-
-The Countable
interface is implemented using PHP's iterator_count
on the Reader::getRecords
method.
-
-## Records selection
-
-### Simple Usage
-
-```php
-public Reader::fetchColumnByName(string $columnName): Iterator
-public Reader::fetchColumnByIndex(int $columnIndex = 0): Iterator
-public Reader::fetchColumn(string|int $columnIndex = 0): Generator
-public Reader::first(): array
-public Reader::nth(int $nth_record): array
-public Reader::fetchOne(int $nth_record = 0): array
-public Reader::fetchPairs(string|int $offsetIndex = 0, string|int $valueIndex = 1): Generator
-```
-
-Using method overloading, you can directly access all retrieving methods attached to the [ResultSet](/9.0/reader/resultset/#records) object.
-
-#### Example
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-
-$records = $reader->fetchColumnByOffset(2);
-//$records is a Generator representing all the fields of the CSV 3rd column
-```
-
-### Advanced Usage
-
-If you require a more advance record selection, you should use a [Statement](/9.0/reader/statement/) object to process the `Reader` object. The found records are returned as a [ResultSet](/9.0/reader/resultset) object.
-
-#### Example
-
-```php
-use League\Csv\Reader;
-use League\Csv\Statement;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$stmt = (new Statement())
- ->offset(3)
- ->limit(5)
-;
-
-$records = $stmt->process($reader);
-//$records is a League\Csv\ResultSet object
-```
-
-## Collection methods
-
-New since version 9.11.0
-
-To ease working with the loaded CSV document 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 CSV document.
-
-### Reader::each
-
-Iterates over the records in the CSV document and passes each item to a closure:
-
-```php
-use League\Csv\Reader;
-use League\Csv\Writer;
-
-$writer = Writer::createFromString('');
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$reader->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 $reader document.
-// the iteration stopped when the closure return false.
-```
-
-### Reader::exists
-
-Tests for the existence of an element that satisfies the given predicate.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$exists = $reader->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.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$nbTotalCells = $reader->recude(fn (?int $carry, array $records) => ($carry ?? 0) + count($records));
-
-//$records contains the total number of celle contains in the CSV documents.
-```
-
-### 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
.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$records = $reader->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
.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$records = $reader->slice(10, 25);
-
-//$records contains up to 25 rows starting at the offest 10 (the eleventh rows)
-```
-
-### Reader::sorted
-
-Sorts the CSV document while keeping the original keys.
-
-```php
-use League\Csv\Reader;
-
-$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
-$records = $reader->sorted(fn (array $recordA, array $recordB) => $recordA['firstname'] <=> $recordB['firstname']);
-
-//$records is a ResultSet containing the sorted CSV document.
-//The original $reader is not changed
-```
+## Selecting records
- Wraps the functionality of Statement::orderBy
.
+Please header over the [TabularDataReader documentation page](/9.0/reader/tabular-data-reader)
+for more information on the class features. If you require a more advance record selection, you
+should use a [Statement or a FragmentFinder](/9.0/reader/statement/) class to process the `Reader` object. The
+found records are returned as a [ResultSet](/9.0/reader/resultset) object.
## Records conversion
### Json serialization
-The `Reader` 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 `Reader::getRecords` method. As such, the returned `JSON` string data depends on the presence or absence of a header.
+The `Reader` 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 `Reader::getRecords` method. As such, the returned `JSON`
+string data depends on the presence or absence of a header.
```php
use League\Csv\Reader;
@@ -629,8 +439,11 @@ echo json_encode($result, JSON_PRETTY_PRINT), PHP_EOL;
The record offset is not preserved on conversion
-To convert your CSV to JSON
you must be sure its content is UTF-8
encoded, using, for instance, the library CharsetConverter stream filter.
+To convert your CSV 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](/9.0/converter/) bundled with this library.
+If you wish to convert your CSV document in `XML` or `HTML` please refer to the [converters](/9.0/converter/) bundled
+with this library.
diff --git a/docs/9.0/reader/resultset.md b/docs/9.0/reader/resultset.md
index 30d0b98b..a2a6bf74 100644
--- a/docs/9.0/reader/resultset.md
+++ b/docs/9.0/reader/resultset.md
@@ -5,515 +5,26 @@ title: Accessing Records from a CSV document
# Result Set
-A `League\Csv\ResultSet` object represents the associated result set of processing a [CSV document](/9.0/reader/) with a [constraint builder](/9.0/reader/statement/). This object is returned from [Statement::process](/9.0/reader/statement/#apply-the-constraints-to-a-csv-document) execution.
+A `League\Csv\ResultSet` object represents the associated result set of processing a [CSV document](/9.0/reader/) with a [constraint builder](/9.0/reader/statement/).
+This object is returned from [Statement::process](/9.0/reader/statement/#apply-the-constraints-to-a-csv-document) 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
+## Selecting records
-### Accessing the result set column names
-
-```php
-public ResultSet::getHeader(): array
-```
-
-`ResultSet::getHeader` returns the header associated with the current object.
-
-#### Example: no header information was given
-
-```php
-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
-
-```php
-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
-
-```php
-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.
-
-```php
-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
-
-```php
-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:
-
-```php
-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
-}
-```
-
-since 9.12.0
the optional $header
is a full mapper
-
-The argument now links the records column offset to a specific column name. In other words this means
-that the array key which MUST be a positive integer or `0` will correspond to the CSV column offset
-and its value will represent its header value.
-
-This means that you can re-arrange the column order as well as removing or adding column to the returned iterator.
-Added column will only contain the `null` value.
-
-Here's an example of the new behaviour.
-
-```php
-use League\Csv\Reader;
-
-$csv = <<process($reader);
-$records = $resultSet->getRecords([3 => 'Year', 0 => 'Firstname', 4 => 'Yolo']);
-var_dump([...$records][0]);
-//returns something like this
-// array:4 [
-// "Year" => "2004",
-// "Firstname" => "Abel",
-// "Yolo" => null,
-// ]
-```
-
-As you can see the `Count` column is missing, the `Year` and `Firstname` columns are re-arranged but
-present and the extra `Yolo` column is added with the value `null`
-
-### Usage with the header
-
-If the `ResultSet::getHeader` is not an empty `array` the found records keys will contain the returned values.
-
-```php
-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`.
-
-```php
-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.
-
-```php
-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
-
-```php
-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`
-
-```php
-$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.
-
-```php
-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`;
-
-```php
-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.
-
-```php
-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:
-
-- an integer representing the column index starting from `0`;
-- a string representing one of the value of `ResultSet::getHeader`;
-
-```php
-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'
-}
-```
-
-```php
-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.
-
-```php
-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.
-
-```php
-public ResultSet::fetchPairs(string|int $offsetIndex = 0, string|int $valueIndex = 1): Generator
-```
-
-Both arguments, `$offsetIndex` and `$valueIndex` can be:
-
-- an integer which represents the column name index;
-- a string representing the value of a column name;
-
-These arguments behave exactly like the `$columnIndex` from `ResultSet::fetchColumn`.
-
-```php
-use League\Csv\Reader;
-use League\Csv\Statement;
-
-$str = <<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 no `$offsetIndex` is provided it defaults to `0`;
-- If no `$valueIndex` is provided it defaults to `1`;
-- If no cell is found corresponding to `$offsetIndex` the row is skipped;
-- If no cell is found corresponding to `$valueIndex` the `null` value is used;
-
-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:
-
-```php
-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.
-
-```php
-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.
-
-```php
-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
.
-
-```php
-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
.
-
-```php
-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
.
-
-```php
-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
-```
+Please header over the [TabularDataReader documentation page](/9.0/reader/tabular-data-reader)
+for more information on the class features. If you require a more advance record selection, you
+should use a [Statement or a FragmentFinder](/9.0/reader/statement/) class to process the `Reader` object. The
+found records are returned as a [ResultSet](/9.0/reader/resultset) object.
## 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.
+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.
```php
use League\Csv\Reader;
@@ -548,7 +59,6 @@ echo json_encode($result, JSON_PRETTY_PRINT), PHP_EOL;
```
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
diff --git a/docs/9.0/reader/statement.md b/docs/9.0/reader/statement.md
index b887f82b..d91ca29f 100644
--- a/docs/9.0/reader/statement.md
+++ b/docs/9.0/reader/statement.md
@@ -3,142 +3,165 @@ layout: default
title: CSV document constraint Builder
---
-# Constraint Builder
+# Constraint Builders
-The `League\Csv\Statement` class is a constraint builder to help ease selecting records from a CSV document created using the `League\Csv\Reader` class.
+The package provides two (2) convenient ways to query the `Reader` and the `ResultSet` instances. They
+can be used to perform manipulation independently of the instance giving you more controls over
+which records you want to access from your input document.
-When building a constraint, the methods do not need to be called in any particular order, and may be called multiple times. Because the `Statement` object is immutable, each time its constraint methods are called they will return a new `Statement` object without modifying the current `Statement` object.
+## Statement
-Because the Statement
object is independent of the Reader
object it can be re-used on multiple Reader
objects.
+The first mechanism is the `League\Csv\Statement` class which is a constraint builder that more or less
+mimic the behaviour of query builders in the database world. It can filter, order and limit the records
+to be shown. It does so by adding and combining constraints. Once the constraint is built, it will
+process your input and always return a [ResultSet](/9.0/reader/resultset) instance. Of note, the resulting constraint
+can be applied on multiple documents as the instance is immutable and completely independent of
+the input.
-Starting with version 9.6.0
, the class exposes the Statement::create
named constructor to ease object creation.
+### Retrieving all the rows
-## Filtering constraint
+Starting with version 9.6.0
, the class exposes the
+Statement::create
named constructor to ease object creation.
-The filters attached using the `Statement::where` method **are the first settings applied to the CSV before anything else**. This option follows the *First In First Out* rule.
+To start using the `Statement` class you should use the `create` method. It returns a valid instance
+ready to already process your document or on which you can add more constraints. Because the
+`Statement` object is immutable, each time its constraint methods are called they will
+return a new `Statement` object without modifying the current `Statement` object.
+Once your constraint is ready to be used, use its `process` method on a `TabularDataReader` class.
```php
-public Statement::where(callable $callable): self
-```
-
-The callable filter signature is as follows:
+use League\Csv\Reader;
+use League\Csv\Statement;
-```php
-function(array $record [, int $offset [, Iterator $iterator]]): self
+$reader = Reader::createFromPath('/path/to/file.csv');
+$records = Statement::create()->process($reader);
+// $records is a League\Csv\ResultSet instance
```
-It takes up to three parameters:
+The `process` method returns a new `TabularDataReader` on which each constraint have been applied.
+If no constraint has been added the return object will contain the same data as its input.
-- `$record`: the current CSV record as an array
-- `$offset`: the current CSV record offset
-- `$iterator`: the current CSV iterator
+Warning: since version 9.12.0
the optional
+$header
argument used by the process
method is deprecated.
-## Sorting constraint
+### Where clauses
-The sorting options are applied **after the Statement::where options**. The sorting follows the *First In First Out* rule.
-
-Warning: To sort the data iterator_to_array
is used, which could lead to a performance penalty if you have a heavy CSV file to sort
-
-`Statement::orderBy` method adds a sorting function each time it is called.
+To filter the records from your input you may use the `where` method. The method can be
+called multiple time and each time it will add another constraint filter. This option
+follows the *First In First Out* rule. The filter excepts a callable similar to the
+one used by `array_filter`. For example the following filter will remove all the
+records whose `3rd` field does not contain a valid `email`:
```php
-public Statement::orderBy(callable $callable): self
+use League\Csv\Reader;
+use League\Csv\Statement;
+
+$reader = Reader::createFromPath('/path/to/file.csv');
+$records = Statement::create()
+ ->where(fn (array $record): bool => false !== filter_var($record[2] ?? '', FILTER_VALIDATE_EMAIL))
+ ->process($reader);
+// $records is a League\Csv\ResultSet instance
```
-The callable sort function signature is as follows:
+### Ordering
+
+The `orderBy` method allows you to sort the results of the applied constraints. Just like
+with filtering the method can be called multiple and the *First In First Out* rule is
+also applied. The callable accepted is similar to the one used by the `usort` function.
+As an example let's order the records according to the lastname found on the records.
```php
-function(array $recordA, array $recordB): int
-```
+use League\Csv\Reader;
+use League\Csv\Statement;
-The sort function takes exactly two parameters, which will be filled by pairs of records.
+$reader = Reader::createFromPath('/path/to/file.csv');
+$records = Statement::create()
+ ->orderBy(fn (array $rA, $rB): int => strcmp($rB[1] ?? '', $rA[1] ?? '')))
+ ->process($reader);
+// $records is a League\Csv\ResultSet instance
+```
-## Interval constraint
+Warning: To sort the data iterator_to_array
is used,
+which could lead to a performance penalty if you have a heavy CSV file to sort
-The interval methods enable returning a specific interval of CSV records. When called more than once, only the last filtering setting is taken into account. The interval is calculated **after applying Statement::orderBy options**.
+### Limit and Offset
-The interval API is made of the following methods:
+You can use the `limit` and `offset` methods to limit the number of records returned. When called more than once,
+only the last filtering setting will be taken into account. The `offset` specifies an optional offset for
+the returned data. By default, if no offset is provided the offset equals `0`. On the other hand, the
+`limit` method specifies an optional maximum records count for the returned data. By default, if
+no limit is provided the limit equals `-1`, which translates to all records. We can for instance
+limit the number of records to at most `5` starting from the `10`th found record.
```php
-public Statement::offset(int $offset): self
-public Statement::limit(int $limit): self
-```
-
-`Statement::offset` specifies an optional offset for the returned data. By default, if no offset is provided the offset equals `0`.
+use League\Csv\Reader;
+use League\Csv\Statement;
-`Statement::limit` specifies an optional maximum records count for the returned data. By default, if no limit is provided the limit equals `-1`, which translates to all records.
+$reader = Reader::createFromPath('/path/to/file.csv');
+$records = Statement::create()
+ ->limit(5)
+ ->offset(9)
+ ->process($reader);
+// $records is a League\Csv\ResultSet instance
+```
When called multiple times, each call overrides the last setting for these options.
-## Processing a CSV document
+## FragmentFinder
-```php
-public Statement::process(Reader $reader, array $header = []): ResultSet
-```
+This mechanism is introduced with version 9.12.0
.
-This method processes a [Reader](/9.0/reader/) object and returns the found records as a [ResultSet](/9.0/reader/resultset) object.
+The second mechanism is based on [RFC7111](https://www.rfc-editor.org/rfc/rfc7111) and allow selecting
+part of your document according to its rows, columns or cells coordinates. The RFC, and thus, our class
+assume that your data is column size consistant and, in absence of a specified header, it will use the
+first record as reference to determine the input number of columns.
-```php
-use League\Csv\Reader;
-use League\Csv\Statement;
+The RFC defines three (3) types of selections and the `FragmentFinder` class supports them all.
-function filterByEmail(array $record): bool
-{
- return (bool) filter_var($record[2], FILTER_VALIDATE_EMAIL);
-}
-
-function sortByLastName(array $recordA, array $recordB): int
-{
- return strcmp($recordB[1], $recordA[1]);
-}
-
-$reader = Reader::createFromPath('/path/to/file.csv', 'r');
-$stmt = (new Statement())
- ->offset(3)
- ->limit(2)
- ->where('filterByEmail')
- ->orderBy('sortByLastName')
-;
-
-$records = $stmt->process($reader);
-```
+You can select part of your data according to:
-Just like the `Reader:getRecords`, the `Statement::process` method takes an optional `$header` argument to allow mapping CSV field names to a user defined header record.
+- its row index using an expression that starts with the `row` keyword;
+- its column index using an expression that starts with the `col` keyword;
+- its cell coordinates using an expression that starts with the `cell` keyword;
-Using the $header
argument is deprecated since version 9.12.0
,
-use instead the TabularDataReader::getRecords
method instead on the returned value.
-A E_USER_DEPRECATED
notice will be triggered if the argument is used.
+Here are some selection example:
-```php
-use League\Csv\Reader;
-use League\Csv\Statement;
+- `col=5` : will select the column `4`;
+- `col=5-7` : will select the columns `4` to `6` included;
+- `row=5-*` : will select all the remaining rows of the document starting from the `4th` row.
+- `cell=5,2-8,9` : will select the cells located between row `4` and column `1` and row `7` and column `8`;
-$reader = Reader::createFromPath('/path/to/file.csv', 'r');
-$stmt = Statement::create()
- ->offset(3)
- ->limit(2)
- ->where(fn(array $record) => (bool) filter_var($record[2], FILTER_VALIDATE_EMAIL))
- ->orderBy(fn(array $recordA, array $recordB) => strcmp($recordB[1], $recordA[1]))
-;
+Of note, the RFC allows for multiple disjonctive selections, separated by a `;`. To strictly
+cover The RFC the class exposes the `all` method which returns an iterator containing the
+results of all found fragments as distinct `TabulatDataReader` instances.
-$records = $stmt->process($reader, ['firstname', 'lastname', 'email']);
-```
+If some selections are invalid no error is returned; the invalid
+selection is skipped from the returned value.
+
+To restrict the returned values you may use the `first` and `firstOrFail` methods. Both methods
+return on success a `TabularDataReader` instance. While the `first` method always return the
+first selection found or `null`; `firstOrFail` **MUST** return a `TabularDataReader` instance
+or throw. It will also throw if the expression syntax is invalid while all the other methods
+just ignore the error.
-Starting with version 9.6.0
, the Statement::process
method can also be used on the ResultSet
class because it implements the TabularDataReader
interface.
+For example, with the following partially invalid expression:
```php
use League\Csv\Reader;
-use League\Csv\Statement;
-
-$reader = Reader::createFromPath('/path/to/file.csv', 'r');
-$stmt = Statement::create()
- ->where(fn(array $record) => (bool) filter_var($record[2], FILTER_VALIDATE_EMAIL))
- ->orderBy(fn(array $recordA, array $recordB) => strcmp($recordB[1], $recordA[1]))
-;
+use League\Csv\FragmentFinder;
-$resultSet = $stmt->process($reader, ['firstname', 'lastname', 'email']);
+$reader = Reader::createFromPath('/path/to/file.csv');
+$finder = new FragmentFinder();
-$stmt2 = Statement::create(null, 3, 2);
-$records = $stmt2->process($resultSet);
-//the $records and $resultSet variables are distinct League\Csv\ResultSet instances.
+$finder->all('row=7-5;8-9', $reader); // return an Iterator
+$finder->first('row=7-5;8-9', $reader); // return an TabulatDataReader
+$finder->firstOrFail('row=7-5;8-9', $reader); // will throw
```
+
+- `FragmentFinder::all` returns an Iterator containing a single `TabularDataReader` because the first selection
+is invalid;
+- `FragmentFinder::first` returns the single valid `TabularDataReader`
+- `FragmentFinder::firstOrFail` throws a `SyntaxError`.
+
+Both classes, `FragmentFinder` and `Statement` returns an instance that implements the `TabularDataReader` interface
+which can be use to return the found data in a consistent way.
diff --git a/docs/9.0/reader/tabular-data-reader.md b/docs/9.0/reader/tabular-data-reader.md
new file mode 100644
index 00000000..639b89b5
--- /dev/null
+++ b/docs/9.0/reader/tabular-data-reader.md
@@ -0,0 +1,390 @@
+---
+layout: default
+title: Tabular Data Reader
+---
+
+# TabularDataReader
+
+Introduced in version `9.6` the `League\Csv\TabularDataReader` interfaces provides a common
+API to works with tabular data like structure. Once implemented, it can be used to work
+with HTML Table, simple RDBMS tables, CSV document and so forth. The only requirement are
+to have:
+
+- a collection or records (preferably consistent in their size);
+- an optional header with unique values;
+
+A good example of what you can achieve can be seen with the following snippet
+
+```php
+use League\Csv\Reader;
+
+$records = Reader::createFromPath('/path/to/file.csv')
+ ->filter(fn (array $record): bool => false !== filter_var($record[2] ?? '', FILTER_VALIDATE_EMAIL))
+ ->select(1, 4, 5)
+ ->slice(3, 5)
+ ->getRecords();
+
+foreach ($record as $record) {
+ //do something meaningful with the found records
+}
+```
+
+Once you created a `TabularDataReader` implementing instance, here we are using the `Reader` you will
+be able to filter, slice and select part of your data to finally access it using the `getRecords` method.
+You will also be able to process the instance using a [Statement](/9.0/reader/statement/) object.
+All these methods are part of the `TabularDataReader` contract. In general, `TabularDataReader` are immutable,
+meaning every `TabularDataReader` method returns an entirely new `TabularDataReader` instance
+leaving your source data unchanged.
+
+## Available methods
+
+While the `TabularDataReader` is not a fully fledged collection instance it still exposes a lots of methods
+that fall into the category of records collection manipulations. Because chaining is at the core of most of
+its method you can be sure that each manipulation returns a new instance preserving your original data.
+
+## Countable, IteratorAggregate
+
+Any `TabularDataReader` instance implements the `Countable` and the `IteratorAggregate` interface.
+It means that at any given time you can access the number of elements that are included in the instance
+as well as iterate over all the record using the `foreach` structure.
+
+```php
+use League\Csv\Reader;
+
+$reader = Reader::createFromPath('/path/to/my/file.csv');
+count($reader); //returns 4
+foreach ($reader as $offset => $record) {
+ //iterates over the 4 records.
+}
+```
+
+## Selection methods
+
+### getHeader
+
+The `getHeader` returns the header associated with the current object. If the current object
+has no header, it will return the empty array.
+
+```php
+use League\Csv\Reader;
+
+$reader = Reader::createFromPath('/path/to/my/file.csv');
+$reader->getHeader(); //is empty because no header information was given
+```
+
+### getRecords
+
+The `getRecords` enables iterating over all records from the current object. If the optional `$header`
+argument is given, it will be used as a mapper on the record and will update the record header
+and the value position.
+
+```php
+use League\Csv\Reader;
+
+$csv = <<process($reader);
+$records = $resultSet->getRecords([3 => 'Year', 0 => 'Firstname', 4 => 'Yolo']);
+var_dump([...$records][0]);
+//returns something like this
+// array:4 [
+// "Year" => "2004",
+// "Firstname" => "Abel",
+// "Yolo" => null,
+// ]
+```
+
+full mapper usage was completed in version 9.12
for Reader
and ResultSet
.
+Added in version 9.6.0
for ResultSet
.
+
+## first and nth
+
+You may access any record using its offset starting at `0` in the collection using the `nth` method.
+if no record is found, an empty `array` is returned.
+
+```php
+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->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->first();
+$result->nth(0);
+//returns the first matching record from the recordset or an empty record if none is found.
+```
+
+As an alias to `nth`, the `first` method returns the first record from the instance without the need of an argument.
+
+Added in version 9.9.0
for Reader
and ResultSet
.
+
+### fetchColumnByName
+
+The `fetchColumnByName` returns an Iterator containing all the values of a single column specified by its header name if it exists.
+
+```php
+$reader = Reader::createFromPath('/path/to/my/file.csv');
+$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'
+}
+```
+
+Added in version 9.8.0
for Reader
and ResultSet
.
+
+### fetchColumnByOffset
+
+The `fetchColumnByOffset` returns an Iterator containing all the values of a single column specified by its
+header offset.
+
+```php
+$reader = Reader::createFromPath('/path/to/my/file.csv');
+$reader->setHeaderOffset(0);
+$records = Statement::create()->process($reader);
+foreach ($records->fetchColumnByName(3) 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'
+}
+```
+
+Added in version 9.8.0
for Reader
and ResultSet
.
+
+### fetchPairs
+
+The `fetchPairs` method returns a Iterator of key-value pairs from two tabular data columns. The method
+expect 2 arguments, both can be:
+
+- an integer which represents the column name index;
+- a string representing the value of a column name;
+
+These arguments behave exactly like the `$columnIndex` from `ResultSet::fetchColumnByName`
+and `ResultSet::fetchColumnByOffset`.
+
+```php
+use League\Csv\Reader;
+use League\Csv\Statement;
+
+$str = <<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 no `$offsetIndex` is provided it defaults to `0`;
+- If no `$valueIndex` is provided it defaults to `1`;
+- If no cell is found corresponding to `$offsetIndex` the row is skipped;
+- If no cell is found corresponding to `$valueIndex` the `null` value is used;
+
+If the TabularDataReader
contains column names and the submitted arguments are not found, an Exception
exception is thrown.
+
+### exists
+
+Tests for the existence of an element that satisfies a given predicate.
+
+```php
+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 least one cell contains the word `twenty-five` otherwise returns false,
+```
+
+Added in version 9.11.0
for Reader
and ResultSet
.
+
+## Functional methods
+
+### each
+
+The `each` method iterates over the records in the tabular data collection and passes each reacord to a
+closure.
+
+```php
+use League\Csv\Reader;
+use League\Csv\Writer;
+
+$writer = Writer::createFromString('');
+$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
+$reader->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 $reader document.
+// the iteration stopped when the closure return false.
+```
+
+You may interrupt the iteration if the closure passed to `each` returns `false`.
+
+Added in version 9.11.0
for Reader
and ResultSet
.
+
+### reduce
+
+The `reduce` method reduces the tabular data structure to a single value, passing
+the result of each iteration into the subsequent iteration:
+
+```php
+use League\Csv\Reader;
+use League\Csv\ResultSet;
+
+$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
+$resultSet = ResultSet::createFromTabularDataReader($reader);
+
+$nbTotalCells = $resultSet->recude(fn (?int $carry, array $records) => ($carry ?? 0) + count($records));
+
+//$records contains the total number of celle contains in the $resultSet
+```
+
+The closure is similar as the one used with `array_reduce`.
+
+Added in version 9.11.0
for Reader
and ResultSet
.
+
+## Collection methods
+
+The following methods return all a new `TabularDataReader` instance.
+They effectively allow selecting a range of records or columns contained
+within the `TabularDataReader` schema.
+
+### filter
+
+Returns all the elements of this collection for which your callback function returns `true`. The order and
+keys of the elements are preserved.
+
+```php
+use League\Csv\Reader;
+
+$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
+$records = $reader->filter(fn (array $record): => 5 === count($record));
+
+//$recors is a ResultSet object with only records with 5 elements
+```
+
+ Wraps the functionality of Statement::where
.
+Added in version 9.11.0
for Reader
and ResultSet
.
+
+### sorted
+
+Sorts the CSV document while keeping the original keys.
+
+```php
+use League\Csv\Reader;
+
+$reader = Reader::createFromPath('/path/to/my/file.csv', 'r');
+$records = $reader->sorted(fn (array $recordA, array $recordB) => $recordA['firstname'] <=> $recordB['firstname']);
+
+//$records is a ResultSet containing the sorted CSV document.
+//The original $reader is not changed
+```
+
+ Wraps the functionality of Statement::orderBy
.
+Added in version 9.11.0
for Reader
and ResultSet
.
+
+### 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.
+
+```php
+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 is a TabularDataReader which contains up to 25 rows
+//starting at the offset 10 (the eleventh rows)
+```
+
+ Wraps the functionality of Statement::offset
and Statement::limit
.
+Added in version 9.11.0
for Reader
and ResultSet
.
+
+### select
+
+You may not always want to select all columns from the tabular data. Using the `select` method,
+you can specify which columns to use. The column can be specified by their
+name if the instance `getHeader` returns a non-empty array or you can use
+the column offset or mix them both.
+
+```php
+use League\Csv\Reader;
+
+$reader = Reader::createFromPath('/path/to/my/file.csv')
+ ->select(2, 5, 8);
+
+//$reader is a new TabularDataReader with 3 columns
+```
+
+Added in version 9.12.0
for Reader
and ResultSet
.
+
+### matching, firstMatching, firstOrFailMatching
+
+The `matching` method allows selecting all records or cells from the tabular data reader that match the
+RFC7111 expression and returns a new collection containing these elements without preserving the keys.
+The method wraps the functionality of `FragmentFinder::all`. Conversely, `firstMatchingz`
+wraps the functionality of `FragmentFinder::first` and last but not least,
+`FragmentFinder::firstOrFail` behaviour is wrap inside the `firstOrFailMatching` method.
+
+```php
+use League\Csv\Reader;
+
+$reader = Reader::createFromString($csv);
+
+$reader->matching('row=3-1;4-6'); //returns an Iterator contains all the TabularDataReader instance that are valid.
+$reader->firstMatching('row=3-1;4-6'); // will return 1 selected fragment as a TabularRaeaderData instance
+$reader->firstOrFailMatching('row=3-1;4-6'); // will throw
+```
+
+ Wraps the functionality of FragmentFinder
class.
+Added in version 9.12.0
for Reader
and ResultSet
.
diff --git a/docs/_data/menu.yml b/docs/_data/menu.yml
index a061c523..142d0fee 100644
--- a/docs/_data/menu.yml
+++ b/docs/_data/menu.yml
@@ -15,8 +15,9 @@ version:
Writer Connection: '/9.0/writer/'
Bundled Helpers: '/9.0/writer/helpers/'
Selecting Records:
+ Contract: '/9.0/reader/tabular-data-reader/'
+ Constraint Builders: '/9.0/reader/statement/'
Reader Connection: '/9.0/reader/'
- Constraint Builder: '/9.0/reader/statement/'
Result Set: '/9.0/reader/resultset/'
Interoperability:
Overview : '/9.0/interoperability/'