Skip to content

Commit

Permalink
Making the TabularDataReader getRecords optional argument a proper ma…
Browse files Browse the repository at this point in the history
…pper
  • Loading branch information
nyamsprod committed Sep 30, 2023
1 parent 94bff98 commit 4433d1b
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 57 deletions.
47 changes: 16 additions & 31 deletions src/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@
use JsonSerializable;
use SplFileObject;

use function array_combine;
use function array_filter;
use function array_pad;
use function array_slice;
use function array_unique;
use function count;
use function is_array;
use function iterator_count;
use function mb_strlen;
Expand Down Expand Up @@ -325,18 +321,16 @@ public function sorted(Closure $orderBy): TabularDataReader
}

/**
* @param array<string> $header
* @param array<string> $headerMapper
*
* @throws Exception
*
* @return Iterator<array<string|null>>
*/
public function getRecords(array $header = []): Iterator
public function getRecords(array $headerMapper = []): Iterator
{
$header = $this->computeHeader($header);

$headerMapper = $this->computeHeader($headerMapper);
$normalized = fn ($record): bool => is_array($record) && ($this->is_empty_records_included || $record !== [null]);

$bom = '';
if (!$this->is_input_bom_included) {
$bom = $this->getInputBOM();
Expand All @@ -348,13 +342,10 @@ public function getRecords(array $header = []): Iterator
}

if ($this->is_empty_records_included) {
$records = new MapIterator(
$records,
fn (array $record): array => ([null] === $record) ? [] : $record
);
$records = new MapIterator($records, fn (array $record): array => ([null] === $record) ? [] : $record);
}

return $this->combineHeader($records, $header);
return $this->combineHeader($records, $headerMapper);
}

/**
Expand Down Expand Up @@ -382,33 +373,27 @@ protected function computeHeader(array $header): array
/**
* Combines the CSV header to each record if present.
*
* @param array<string> $header
* @param array<string> $headerMapper
*/
protected function combineHeader(Iterator $iterator, array $header): Iterator
protected function combineHeader(Iterator $iterator, array $headerMapper): Iterator
{
$formatter = fn (array $record): array => array_reduce(
$this->formatters,
fn (array $record, callable $formatter): array => $formatter($record),
$record
);

if ([] === $header) {
return new MapIterator($iterator, $formatter(...));
}

$field_count = count($header);
$mapper = function (array $record) use ($header, $field_count, $formatter): array {
if (count($record) !== $field_count) {
$record = array_slice(array_pad($record, $field_count, null), 0, $field_count);
}
return match ([]) {
$headerMapper => new MapIterator($iterator, $formatter(...)),
default => new MapIterator($iterator, function (array $record) use ($headerMapper, $formatter): array {
$assocRecord = [];
foreach ($headerMapper as $offset => $headerName) {
$assocRecord[$headerName] = $record[$offset] ?? null;
}

/** @var array<string|null> $assocRecord */
$assocRecord = array_combine($header, $record);

return $formatter($assocRecord);
return $formatter($assocRecord);
}),
};

return new MapIterator($iterator, $mapper);
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/ReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -646,4 +646,17 @@ public function testReaderFormatterUsesOffset(): void
],
], [...$reader]);
}

public function testHeaderMapper(): void
{
$csv = <<<CSV
Abel,14,M,2004
Abiga,6,F,2004
Aboubacar,8,M,2004
Aboubakar,6,M,2004
CSV;
$firstRow = [...Reader::createFromString($csv)
->getRecords([3 => 'Year', 0 => 'Firstname', 1 => 'Count'])][0];
self::assertSame(['Year' => '2004', 'Firstname' => 'Abel', 'Count' => '14'], $firstRow);
}
}
47 changes: 23 additions & 24 deletions src/ResultSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,19 @@
*/
class ResultSet implements TabularDataReader, JsonSerializable
{
/** @var array<string> */
protected array $header;

/**
* @param Iterator<array-key, array<array-key, string|null>> $records
* @param array<string> $header
*
* @throws SyntaxError
*/
public function __construct(protected Iterator $records, protected array $header = [])
public function __construct(protected Iterator $records, array $header = [])
{
$this->validateHeader($this->header);
$this->validateHeader($header);
$this->header = array_values($header);
}

/**
Expand Down Expand Up @@ -150,45 +154,40 @@ public function sorted(Closure $orderBy): TabularDataReader
}

/**
* @param array<string> $header
* @param array<string> $headerMapper
*
* @throws SyntaxError
*
* @return Iterator<array-key, array<array-key, string|null>>
*/
public function getRecords(array $header = []): Iterator
public function getRecords(array $headerMapper = []): Iterator
{
$this->validateHeader($header);
$this->validateHeader($headerMapper);

yield from $this->combineHeader($header);
yield from $this->combineHeader($headerMapper);
}

/**
* Combines the header to each record if present.
*
* @param array<string> $header
* @param array<string> $headerMapper
*
* @return Iterator<array-key, array<array-key, string|null>>
*/
protected function combineHeader(array $header): Iterator
protected function combineHeader(array $headerMapper): Iterator
{
if ($header === $this->header || [] === $header) {
return $this->records;
}

$field_count = count($header);
$mapper = static function (array $record) use ($header, $field_count): array {
if (count($record) !== $field_count) {
$record = array_slice(array_pad($record, $field_count, null), 0, $field_count);
}

/** @var array<string, string|null> $assocRecord */
$assocRecord = array_combine($header, $record);

return $assocRecord;
return match (true) {
$headerMapper === $this->header,
[] === $headerMapper => $this->records,
default => new MapIterator($this->records, function (array $record) use ($headerMapper): array {
$assocRecord = [];
foreach ($headerMapper as $offset => $headerName) {
$assocRecord[$headerName] = $record[$offset] ?? null;
}

return $assocRecord;
}),
};

return new MapIterator($this->records, $mapper);
}

public function count(): int
Expand Down
13 changes: 13 additions & 0 deletions src/ResultSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,4 +410,17 @@ public function testExistsRecord(): void
self::assertFalse(Statement::create()->process($this->csv)->exists(fn (array $record) => array_key_exists('foobar', $record)));
self::assertTrue(Statement::create()->process($this->csv)->exists(fn (array $record) => count($record) < 5));
}

public function testHeaderMapperOnResultSet(): void
{
$results = Statement::create()
->process($this->csv)
->getRecords([2 => 'e-mail', 1 => 'lastname', 33 => 'does not exists']);

self::assertSame([
'e-mail' => '[email protected]',
'lastname' => 'doe',
'does not exists' => null,
], [...$results][0]);
}
}
12 changes: 12 additions & 0 deletions src/StatementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,16 @@ public function testOrderByWithEquity(): void

self::assertSame($this->expected, array_values([...$calculated]));
}

public function testHeaderMapperOnStatement(): void
{
$results = Statement::create()
->process($this->csv, [2 => 'e-mail', 1 => 'lastname', 33 => 'does not exists']);
self::assertSame(['e-mail', 'lastname', 'does not exists'], $results->getHeader());
self::assertSame([
'e-mail' => '[email protected]',
'lastname' => 'doe',
'does not exists' => null,
], $results->first());
}
}
4 changes: 2 additions & 2 deletions src/TabularDataReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ public function getHeader(): array;
* filled with null values while extra record fields are strip from
* the returned object.
*
* @param array<string> $header an optional header to use instead of the CSV document header
* @param array<string> $headerMapper an optional header mapper to use instead of the CSV document header
*
* @return Iterator<array-key,array<string|null>>
*/
public function getRecords(array $header = []): Iterator;
public function getRecords(array $headerMapper = []): Iterator;

/**
* Returns the next key-value pairs from the tabular data (first
Expand Down

0 comments on commit 4433d1b

Please sign in to comment.