diff --git a/src/Reader.php b/src/Reader.php index 0e4fd286..7ba53d68 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -434,7 +434,7 @@ public function matchingFirstOrFail(string $expression): TabularDataReader public function select(string|int ...$columns): TabularDataReader { - return Statement::create()->select(...$columns)->process($this); + return ResultSet::createFromTabularDataReader($this)->select(...$columns); } /** diff --git a/src/ResultSet.php b/src/ResultSet.php index 0ac4fc59..2c64db05 100644 --- a/src/ResultSet.php +++ b/src/ResultSet.php @@ -18,15 +18,20 @@ use Closure; use Generator; use Iterator; +use IteratorIterator; use JsonSerializable; use League\Csv\Serializer\Denormalizer; use League\Csv\Serializer\MappingFailed; use League\Csv\Serializer\TypeCastingFailed; use LimitIterator; +use Traversable; use function array_filter; use function array_flip; +use function array_key_exists; +use function array_reduce; use function array_search; +use function array_values; use function is_string; use function iterator_count; @@ -90,13 +95,17 @@ public static function createFromTabularDataReader(TabularDataReader $reader): s } /** - * Returns a new instance from a collection without header.. + * Returns a new instance from a collection without header. * * @throws SyntaxError */ - public static function createFromRecords(Iterator|array $records = []): self + public static function createFromRecords(iterable $records = []): self { - return new self($records); + return new self(match (true) { + $records instanceof Iterator => $records, + $records instanceof Traversable => new IteratorIterator($records), + default => new ArrayIterator($records), + }); } /** @@ -218,7 +227,46 @@ public function sorted(Query\Sort|Closure $orderBy): TabularDataReader public function select(string|int ...$columns): TabularDataReader { - return Statement::create()->select(...$columns)->process($this); + if ([] === $columns) { + return $this; + } + + $recordsHeader = $this->getHeader(); + $hasHeader = [] !== $recordsHeader; + $selectColumn = function (array $header, string|int $field) use ($recordsHeader, $hasHeader): array { + if (is_string($field)) { + $index = array_search($field, $recordsHeader, true); + if (false === $index) { + throw InvalidArgument::dueToInvalidColumnIndex($field, 'offset', __METHOD__); + } + + $header[$index] = $field; + + return $header; + } + + if ($hasHeader && !array_key_exists($field, $recordsHeader)) { + throw InvalidArgument::dueToInvalidColumnIndex($field, 'offset', __METHOD__); + } + + $header[$field] = $recordsHeader[$field] ?? $field; + + return $header; + }; + + /** @var array $header */ + $header = array_reduce($columns, $selectColumn, []); + $callback = function (array $record) use ($header): array { + $element = []; + $row = array_values($record); + foreach ($header as $offset => $headerName) { + $element[$headerName] = $row[$offset] ?? null; + } + + return $element; + }; + + return new self(new MapIterator($this, $callback), $hasHeader ? $header : []); } public function matching(string $expression): iterable diff --git a/src/Statement.php b/src/Statement.php index 73c71695..d5f5c0be 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -25,20 +25,13 @@ use function array_reduce; use function array_search; use function array_values; -use function debug_backtrace; -use function end; use function is_string; -use function func_num_args; -use function trigger_error; - -use const DEBUG_BACKTRACE_IGNORE_ARGS; -use const E_USER_DEPRECATED; /** * Criteria to filter a {@link TabularDataReader} object. * - * @phpstan-import-type ConditionExtended from \League\Csv\Query\PredicateCombinator - * @phpstan-import-type OrderingExtended from \League\Csv\Query\SortCombinator + * @phpstan-import-type ConditionExtended from Query\PredicateCombinator + * @phpstan-import-type OrderingExtended from Query\SortCombinator */ class Statement { @@ -64,11 +57,6 @@ class Statement */ public static function create(?callable $where = null, int $offset = 0, int $limit = -1): self { - if (0 === func_num_args()) { - return new self(); - } - - self::triggerDeprecation('9.16.0', 'Using arguments with the `' . __METHOD__ . '` method is deprecated'); $stmt = new self(); if (null !== $where) { $stmt = $stmt->where($where); @@ -85,20 +73,6 @@ public static function create(?callable $where = null, int $offset = 0, int $lim return $stmt; } - private static function triggerDeprecation(string $version, string $message): void - { - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - $caller = end($backtrace); - if (isset($caller['file'])) { - $message .= '; called from ' . $caller['file']; - } - if (isset($caller['line'])) { - $message .= ' on line ' . $caller['line']; - } - - @trigger_error('Since league\csv '.$version.': '.$message . '.', E_USER_DEPRECATED); - } - /** * Sets the Iterator element columns. */ @@ -327,15 +301,17 @@ public function process(TabularDataReader $tabular_data, array $header = []): Ta $iterator = Query\Limit::new($this->offset, $this->limit)->slice($iterator); } - return match ($this->select) { - [] => new ResultSet($iterator, $header), - default => $this->applySelect($iterator, $header, $this->select), - }; + return (new ResultSet($iterator, $header))->select(...$this->select); } /** + * DEPRECATION WARNING! This method will be removed in the next major point release. + * * @throws InvalidArgument + * * @throws SyntaxError + * @see Statement::process() + * @deprecated Since version 9.16.0 */ protected function applySelect(Iterator $records, array $recordsHeader, array $select): TabularDataReader {