Skip to content

Commit

Permalink
Adding TabularDataReader::value method
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Oct 11, 2023
1 parent b14fe53 commit ccc3e04
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 101 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All Notable changes to `Csv` will be documented in this file

### Added

- `TabulatDataReader::value`
- `TabulatDataReader::select`
- `TabulatDataReader::matching`
- `TabulatDataReader::firstMatching`
Expand Down
27 changes: 26 additions & 1 deletion docs/9.0/reader/tabular-data-reader.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ var_dump([...$records][0]);
<p class="message-notice">full mapper usage was completed in version <code>9.12</code> for <code>Reader</code> and <code>ResultSet</code></code>.</p>
<p class="message-notice">Added in version <code>9.6.0</code> for <code>ResultSet</code>.</p>

### first and nth
### value, 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.
Expand Down Expand Up @@ -143,6 +143,31 @@ As an alias to `nth`, the `first` method returns the first record from the insta

<p class="message-notice">Added in version <code>9.9.0</code> for <code>Reader</code> and <code>ResultSet</code>.</p>

If you are only interested in retrieving a specific value from a single row, you can use
the `value` method. By default, it will return the first record item, but you are free
to specify a specific column using the column name if the header is set and/or the
column offset, If no column is found `null` 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->value(2); //returns '[email protected]'
$result->value('email'); //returns '[email protected]'
$result->value('toto'); //returns null
$result->value(42); //returns null
```

<p class="message-notice">Added in version <code>9.12.0</code> for <code>Reader</code> and <code>ResultSet</code>.</p>

### exists

Tests for the existence of a record that satisfies a given predicate.
Expand Down
16 changes: 12 additions & 4 deletions src/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ public function fetchColumnByOffset(int $offset = 0): Iterator
return ResultSet::createFromTabularDataReader($this)->fetchColumnByOffset($offset);
}

public function value(int|string $column = 0): mixed
{
return match (true) {
is_string($column) => $this->first()[$column] ?? null,
default => array_values($this->first())[$column] ?? null,
};
}

/**
* @throws Exception
*/
Expand Down Expand Up @@ -389,10 +397,10 @@ public function select(string|int ...$columns): TabularDataReader
$header[$field] = $documentHeader[$field] ?? $field;
}

return new ResultSet(
$this->combineHeader($this->prepareRecords(), $this->computeHeader($header)),
$documentHeader
);
/** @var array<int, string> $finalHeader */
$finalHeader = $hasNoHeader ? [] : $header;

return new ResultSet($this->combineHeader($this->prepareRecords(), $this->computeHeader($header)), $finalHeader);
}

/**
Expand Down
33 changes: 31 additions & 2 deletions src/ReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use SplFileObject;
use SplTempFileObject;

Expand All @@ -28,7 +27,7 @@
use function unlink;

#[Group('reader')]
final class ReaderTest extends TestCase
final class ReaderTest extends TabularDataReaderTestCase
{
private Reader $csv;
private array $expected = [
Expand All @@ -51,6 +50,36 @@ protected function tearDown(): void
unset($this->csv);
}

protected function tabularData(): TabularDataReader
{
$csv = <<<CSV
date,temperature,place
2011-01-01,1,Galway
2011-01-02,-1,Galway
2011-01-03,0,Galway
2011-01-01,6,Berkeley
2011-01-02,8,Berkeley
2011-01-03,5,Berkeley
CSV;

return Reader::createFromString($csv);
}

protected function tabularDataWithHeader(): TabularDataReader
{
$csv = <<<CSV
date,temperature,place
2011-01-01,1,Galway
2011-01-02,-1,Galway
2011-01-03,0,Galway
2011-01-01,6,Berkeley
2011-01-02,8,Berkeley
2011-01-03,5,Berkeley
CSV;

return Reader::createFromString($csv)->setHeaderOffset(0);
}

public function testReaderWithEmptyEscapeChar1(): void
{
$source = <<<EOF
Expand Down
17 changes: 14 additions & 3 deletions src/ResultSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,10 @@ public function select(string|int ...$columns): TabularDataReader
$header[$field] = $documentHeader[$field] ?? $field;
}

return new self($this->combineHeader($header), $documentHeader);
/** @var array<int, string> $finalHeader */
$finalHeader = $hasNoHeader ? [] : $header;

return new self($this->combineHeader($header), $finalHeader);
}

public function matching(string $expression): iterable
Expand Down Expand Up @@ -252,7 +255,7 @@ protected function combineHeader(array $header): Iterator
}

return match (true) {
$header === $this->header, [] === $header => $this->records,
[] === $header => $this->records,
default => new MapIterator($this->records, function (array $record) use ($header): array {
$assocRecord = [];
$row = array_values($record);
Expand Down Expand Up @@ -280,13 +283,21 @@ public function first(): array
return $this->nth(0);
}

public function value(int|string $column = 0): mixed
{
return match (true) {
is_string($column) => $this->first()[$column] ?? null,
default => array_values($this->first())[$column] ?? null,
};
}

public function nth(int $nth_record): array
{
if ($nth_record < 0) {
throw InvalidArgument::dueToInvalidRecordOffset($nth_record, __METHOD__);
}

$iterator = new LimitIterator($this->records, $nth_record, 1);
$iterator = new LimitIterator($this->getIterator(), $nth_record, 1);
$iterator->rewind();

/** @var array|null $result */
Expand Down
28 changes: 26 additions & 2 deletions src/ResultSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use SplTempFileObject;

use function current;
Expand All @@ -24,7 +23,7 @@
use function next;

#[Group('reader')]
final class ResultSetTest extends TestCase
final class ResultSetTest extends TabularDataReaderTestCase
{
private Reader $csv;
private Statement $stmt;
Expand All @@ -49,6 +48,31 @@ protected function tearDown(): void
unset($this->csv, $this->stmt);
}

protected function tabularData(): TabularDataReader
{
return new ResultSet([
['date', 'temperature', 'place'],
['2011-01-01', '1', 'Galway'],
['2011-01-02', '-1', 'Galway'],
['2011-01-03', '0', 'Galway'],
['2011-01-01', '6', 'Berkeley'],
['2011-01-02', '8', 'Berkeley'],
['2011-01-03', '5', 'Berkeley'],
]);
}

protected function tabularDataWithHeader(): TabularDataReader
{
return new ResultSet([
['2011-01-01', '1', 'Galway'],
['2011-01-02', '-1', 'Galway'],
['2011-01-03', '0', 'Galway'],
['2011-01-01', '6', 'Berkeley'],
['2011-01-02', '8', 'Berkeley'],
['2011-01-03', '5', 'Berkeley'],
], ['date', 'temperature', 'place']);
}

public function testFilter(): void
{
$func2 = fn (array $row): bool => !in_array('john', $row, true);
Expand Down
1 change: 1 addition & 0 deletions src/TabularDataReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* @method Iterator fetchColumnByOffset(int $offset) returns a column from its offset
* @method array first() returns the first record from the tabular data.
* @method array nth(int $nth_record) returns the nth record from the tabular data.
* @method mixed value(int|string $column = 0) returns a given value from the first element of the tabular data.
* @method bool each(Closure $closure) iterates over each record and passes it to a closure. Iteration is interrupted if the closure returns false
* @method bool exists(Closure $closure) tells whether at least one record satisfies the predicate.
* @method mixed reduce(Closure $closure, mixed $initial = null) reduces the collection to a single value, passing the result of each iteration into the subsequent iteration
Expand Down
47 changes: 0 additions & 47 deletions src/TabularDataReaderReaderTest.php

This file was deleted.

42 changes: 0 additions & 42 deletions src/TabularDataReaderResultSetTest.php

This file was deleted.

10 changes: 10 additions & 0 deletions src/TabularDataReaderTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,14 @@ public function testCountable(): void
self::assertCount(7, $this->tabularData());
self::assertCount(6, $this->tabularDataWithHeader());
}

public function testValue(): void
{
self::assertNull($this->tabularData()->value(42));
self::assertNull($this->tabularData()->value('place'));
self::assertSame('place', $this->tabularData()->value(2));
self::assertSame('2011-01-01', $this->tabularDataWithHeader()->value());
self::assertSame('Galway', $this->tabularDataWithHeader()->value(2));
self::assertSame('Galway', $this->tabularDataWithHeader()->value('place'));
}
}

0 comments on commit ccc3e04

Please sign in to comment.