diff --git a/include/rusty_iterators/interface.hpp b/include/rusty_iterators/interface.hpp index 1978ee0..f223dfd 100644 --- a/include/rusty_iterators/interface.hpp +++ b/include/rusty_iterators/interface.hpp @@ -7,6 +7,7 @@ #include "filter.hpp" #include "filter_map.hpp" #include "inspect.hpp" +#include "interperse.hpp" #include "map.hpp" #include "moving_window.hpp" #include "skip.hpp" @@ -45,6 +46,7 @@ using iterator::Enumerate; using iterator::Filter; using iterator::FilterMap; using iterator::Inspect; +using iterator::Interperse; using iterator::Map; using iterator::MovingWindow; using iterator::Skip; @@ -118,8 +120,9 @@ class IterInterface template requires InspectFunctor - auto inspect(Functor&& f) -> Inspect; + [[nodiscard]] auto inspect(Functor&& f) -> Inspect; + [[nodiscard]] auto interperse(T&& item) -> Interperse; [[nodiscard]] auto last() -> std::optional; template @@ -350,6 +353,13 @@ auto rusty_iterators::interface::IterInterface::inspect(Functor&& f) return Inspect{std::forward(self()), std::forward(f)}; } +template +auto rusty_iterators::interface::IterInterface::interperse(T&& item) + -> Interperse +{ + return Interperse{std::forward(self()), std::forward(item)}; +} + template auto rusty_iterators::interface::IterInterface::last() -> std::optional { diff --git a/include/rusty_iterators/interperse.hpp b/include/rusty_iterators/interperse.hpp new file mode 100644 index 0000000..a556cec --- /dev/null +++ b/include/rusty_iterators/interperse.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "interface.fwd.hpp" + +#include + +namespace rusty_iterators::iterator +{ +using interface::IterInterface; + +template +class Interperse : public IterInterface> +{ + public: + explicit Interperse(Other&& it, T&& item) + : it(std::forward(it)), item(std::forward(item)) + {} + + auto next() -> std::optional; + [[nodiscard]] auto sizeHint() const -> std::optional; + + private: + Other it; + T item; + bool interperse = false; +}; +} // namespace rusty_iterators::iterator + +template +auto rusty_iterators::iterator::Interperse::next() -> std::optional +{ + if (interperse) + { + interperse = false; + return item; + } + + auto nextItem = it.next(); + + if (!nextItem.has_value()) + return std::move(nextItem); + + interperse = true; + return std::move(nextItem); +} + +template +auto rusty_iterators::iterator::Interperse::sizeHint() const -> std::optional +{ + auto underlyingSize = it.sizeHint(); + + if (!underlyingSize.has_value()) + return std::nullopt; + + return underlyingSize.value() * 2; +} diff --git a/tests/interperse.test.cpp b/tests/interperse.test.cpp new file mode 100644 index 0000000..b3993d5 --- /dev/null +++ b/tests/interperse.test.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include + +using ::rusty_iterators::iterator::LazyIterator; +using ::testing::ElementsAreArray; + +TEST(TestInterperseIterator, TestNextReturnsInterpersed) +{ + auto vec = std::vector{1, 2}; + + auto item = 5; + auto it = LazyIterator{vec}.interperse(std::cref(item)); + + ASSERT_EQ(it.next(), 1); + ASSERT_EQ(it.next(), 5); + ASSERT_EQ(it.next(), 2); + ASSERT_EQ(it.next(), 5); + ASSERT_EQ(it.next(), std::nullopt); +} + +TEST(TestInterperseIterator, TestCollectReturnsAllValues) +{ + auto vec = std::vector{1, 2, 3}; + + auto item = 5; + auto it = LazyIterator{vec}.interperse(std::cref(item)); + + EXPECT_THAT(it.collect(), ElementsAreArray({1, 5, 2, 5, 3, 5})); +} + +TEST(TestInterperseIterator, TestSizeHintIsDoubled) +{ + auto vec = std::vector{1, 2, 3}; + + auto item = 5; + auto it = LazyIterator{vec}.interperse(std::cref(item)); + + ASSERT_EQ(it.sizeHint(), 6); +}