Skip to content

Commit eb38e34

Browse files
feat: add skip to iterator interface (#103)
1 parent b9d481c commit eb38e34

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

include/rusty_iterators/interface.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "inspect.hpp"
1010
#include "map.hpp"
1111
#include "moving_window.hpp"
12+
#include "skip.hpp"
1213
#include "take.hpp"
1314
#include "zip.hpp"
1415

@@ -45,6 +46,7 @@ using iterator::FilterMap;
4546
using iterator::Inspect;
4647
using iterator::Map;
4748
using iterator::MovingWindow;
49+
using iterator::Skip;
4850
using iterator::Take;
4951
using iterator::Zip;
5052

@@ -149,6 +151,8 @@ class IterInterface
149151
requires ReduceFunctor<T, Functor>
150152
[[nodiscard]] auto reduce(Functor&& f) -> std::optional<T>;
151153

154+
[[nodiscard]] auto skip(size_t n) -> Skip<T, Derived>;
155+
152156
template <class R = T>
153157
requires Summable<R>
154158
[[nodiscard]] auto sum() -> R;
@@ -443,6 +447,12 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::reduce(Functor&& f)
443447
return fold(std::move(first.value()), std::forward<Functor>(f));
444448
}
445449

450+
template <class T, class Derived>
451+
auto rusty_iterators::interface::IterInterface<T, Derived>::skip(size_t n) -> Skip<T, Derived>
452+
{
453+
return Skip<T, Derived>{std::forward<Derived>(self()), n};
454+
}
455+
446456
template <class T, class Derived>
447457
template <class R>
448458
requires rusty_iterators::concepts::Summable<R>

include/rusty_iterators/skip.hpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#pragma once
2+
3+
#include "interface.fwd.hpp"
4+
5+
#include <algorithm>
6+
#include <optional>
7+
8+
namespace rusty_iterators::iterator
9+
{
10+
using interface::IterInterface;
11+
12+
template <class T, class Other>
13+
class Skip : public IterInterface<T, Skip<T, Other>>
14+
{
15+
public:
16+
explicit Skip(Other&& it, size_t n) : it(std::forward<Other>(it)), n(n) {}
17+
18+
auto next() -> std::optional<T>;
19+
[[nodiscard]] auto sizeHint() const -> std::optional<size_t>;
20+
21+
private:
22+
Other it;
23+
size_t n;
24+
};
25+
} // namespace rusty_iterators::iterator
26+
27+
template <class T, class Other>
28+
auto rusty_iterators::iterator::Skip<T, Other>::next() -> std::optional<T>
29+
{
30+
[[unlikely]] if (n > 0)
31+
{
32+
it = it.advanceBy(n);
33+
n = 0;
34+
return std::move(it.next());
35+
}
36+
return std::move(it.next());
37+
}
38+
39+
template <class T, class Other>
40+
auto rusty_iterators::iterator::Skip<T, Other>::sizeHint() const -> std::optional<size_t>
41+
{
42+
auto underlyingSize = it.sizeHint();
43+
44+
if (!underlyingSize.has_value())
45+
{
46+
return std::nullopt;
47+
}
48+
int32_t estimatedSize = underlyingSize.value() - n;
49+
return std::max(0, estimatedSize);
50+
}

tests/skip.test.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include <gmock/gmock.h>
2+
#include <gtest/gtest.h>
3+
4+
#include <rusty_iterators/iterator.hpp>
5+
6+
using ::rusty_iterators::iterator::LazyIterator;
7+
using ::testing::ElementsAreArray;
8+
9+
TEST(TestSkipIterator, NextSkipsElements)
10+
{
11+
auto vec = std::vector{1, 2, 3, 4, 5};
12+
auto it = LazyIterator{vec}.skip(3);
13+
14+
ASSERT_EQ(it.next(), 4);
15+
ASSERT_EQ(it.next(), 5);
16+
ASSERT_EQ(it.next(), std::nullopt);
17+
}
18+
19+
TEST(TestSkipIterator, NextOnTooSmallIterator)
20+
{
21+
auto vec = std::vector{1, 2, 3};
22+
auto it = LazyIterator{vec}.skip(5);
23+
24+
ASSERT_EQ(it.next(), std::nullopt);
25+
}
26+
27+
TEST(TestSkipIterator, CollectAllItemsExceptSkippedOnes)
28+
{
29+
auto vec = std::vector{1, 2, 3, 4, 5};
30+
auto it = LazyIterator{vec}.skip(3);
31+
32+
EXPECT_THAT(it.collect(), ElementsAreArray({4, 5}));
33+
}
34+
35+
TEST(TestSkipIterator, SizeHintGivesEstimatedSize)
36+
{
37+
auto vec = std::vector{1, 2, 3, 4, 5};
38+
auto it = LazyIterator{vec}.skip(3);
39+
40+
ASSERT_EQ(it.sizeHint(), 2);
41+
}
42+
43+
TEST(TestSkipIterator, SizeHintOnInfiniteIterator)
44+
{
45+
auto vec = std::vector{1, 2, 3};
46+
auto it = LazyIterator{vec}.cycle().skip(3);
47+
48+
ASSERT_EQ(it.sizeHint(), std::nullopt);
49+
}
50+
51+
TEST(TestSkipIterator, TestSizeHintOnTooSmallIterator)
52+
{
53+
auto vec = std::vector{1, 2, 3};
54+
auto it = LazyIterator{vec}.skip(10);
55+
56+
ASSERT_EQ(it.sizeHint(), 0);
57+
}

0 commit comments

Comments
 (0)