diff --git a/README.md b/README.md index 9e38352..6c9711f 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ list the function signatures as an overview: void apply(callable $function, iterable $iterable) string join(string $separator, iterable $iterable) int count(iterable $iterable) + bool isEmpty(iterable $iterable) mixed recurse(callable $function, $iterable) array toArray(iterable $iterable) array toArrayWithKeys(iterable $iterable) diff --git a/src/iter.php b/src/iter.php index 1bb3364..c7d9db9 100644 --- a/src/iter.php +++ b/src/iter.php @@ -3,6 +3,7 @@ namespace iter; use Traversable; +use Countable; /** * Creates an iterable containing all numbers between the start and end value @@ -903,12 +904,12 @@ function join($separator, $iterable) { * iter\count(iter\flatten([1, 2, 3, [4, [[[5, 6], 7]]], 8])) * => 8 * - * @param array|Traversable|\Countable $iterable The iterable to count + * @param array|Traversable|Countable $iterable The iterable to count * * @return int */ function count($iterable) { - if (is_array($iterable) || $iterable instanceof \Countable) { + if (\is_array($iterable) || $iterable instanceof Countable) { return \count($iterable); } if (!$iterable instanceof \Traversable) { @@ -923,6 +924,31 @@ function count($iterable) { return $count; } +/** + * Determines whether iterable is empty. + * + * If the iterable implements Countable, its count() method will be used. + * Calling isEmpty() does not drain iterators, as only the valid() method will + * be called. + * + * @param array|Traversable|Countable $iterable + * @return bool + */ +function isEmpty($iterable) { + if (\is_array($iterable) || $iterable instanceof \Countable) { + return count($iterable) == 0; + } + + if ($iterable instanceof \Iterator) { + return !$iterable->valid(); + } else if ($iterable instanceof \IteratorAggregate) { + return !$iterable->getIterator()->valid(); + } else { + throw new \InvalidArgumentException( + 'Argument must be iterable or implement Countable'); + } +} + /** * Recursively applies a function, working on entire iterables rather than * individual values. diff --git a/test/iterTest.php b/test/iterTest.php index 244ae7d..ef0fe78 100644 --- a/test/iterTest.php +++ b/test/iterTest.php @@ -336,6 +336,15 @@ public function testCount() { $this->assertSame(42, count(new _CountableTestDummy)); } + public function testIsEmpty() { + $this->assertTrue(isEmpty([])); + $this->assertFalse(isEmpty([null])); + $this->assertTrue(isEmpty(toArray([]))); + $this->assertFalse(isEmpty(toArray([null]))); + $this->assertTrue(isEmpty(repeat(42, 0))); + $this->assertFalse(isEmpty(repeat(42))); + } + public function testToArray() { $this->assertSame([1, 2, 3], toArray(['a' => 1, 'b' => 2, 'c' => 3])); $this->assertSame( @@ -503,6 +512,10 @@ function() { _assertAllIterable([[], new \stdClass()]); }, function() { return count(new \stdClass()); }, 'Argument must be iterable or implement Countable' ]; + yield [ + function() { return isEmpty(new \stdClass()); }, + 'Argument must be iterable or implement Countable' + ]; yield [ function() { return toIter(new \stdClass()); }, 'Argument must be iterable'