gielfeldt/iterators

View on GitHub
src/SortIterator.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Gielfeldt\Iterators;

class SortIterator extends TraversableIterator implements \Countable
{
    const SORT_CURRENT = [__CLASS__, 'sortCurrent'];
    const SORT_KEY = [__CLASS__, 'sortKey'];
    const SORT_ASC = 1;
    const SORT_DESC = 2;
    const SORT_REINDEX = 4;

    protected $direction;
    protected $flags;
    protected $callback;
    protected $realCallback;

    public function __construct(\Traversable $iterator, int $direction = self::SORT_ASC, int $flags = 0, callable $callback = self::SORT_CURRENT)
    {
        $this->direction = $direction;
        $this->flags = $flags;
        $this->callback = \Closure::fromCallable($callback);
        $this->realCallback = $direction == self::SORT_ASC ? $this->callback : function ($cmpA, $cmpB) {
            return ($this->callback)($cmpB, $cmpA);
        };
        parent::__construct($this->getSortedIterator($iterator));
    }

    /**
     * @param \Traversable $iterator
     */
    public function getSortedIterator($iterator)
    {
        $sortedIterator = new \ArrayIterator();
        $sorted = [];
        foreach ($iterator as $key => $value) {
            $sorted[] = $this->generateElement($key, $value, $iterator);
        }

        usort($sorted, $this->realCallback);

        foreach ($sorted as $data) {
            $sortedIterator->append($data);
        }
        return $sortedIterator;
    }

    protected function generateElement($key, $value, $iterator)
    {
        return (object) ['key' => $key, 'current' => $value];
    }

    public function key()
    {
        if ($this->flags & self::SORT_REINDEX) {
            return $this->getInnerIterator()->key();
        }
        return $this->getInnerIterator()->current()->key ?? null;
    }

    public function current()
    {
        return $this->getInnerIterator()->current()->current ?? null;
    }

    public function count()
    {
        return $this->getInnerIterator()->count();
    }

    public function first()
    {
        $count = $this->getInnerIterator()->count();
        return $count ? $this->getInnerIterator()[0]->current : null;
    }

    public function last()
    {
        $count = $this->getInnerIterator()->count();
        return $count ? $this->getInnerIterator()[$count - 1]->current : null;
    }

    public function min()
    {
        return $this->direction == self::SORT_ASC ? $this->first() : $this->last();
    }

    public function max()
    {
        return $this->direction == self::SORT_ASC ? $this->last() : $this->first();
    }

    public static function sortCurrent($cmpA, $cmpB)
    {
        return $cmpA->current <=> $cmpB->current;
    }

    public static function sortKey($cmpA, $cmpB)
    {
        return $cmpA->key <=> $cmpB->key;
    }
}