Skip to content

Commit 5bef2f7

Browse files
feat: add zipLongest to iterator interface (#113)
1 parent 3554463 commit 5bef2f7

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

include/rusty_iterators/interface.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "step_by.hpp"
1717
#include "take.hpp"
1818
#include "zip.hpp"
19+
#include "zip_longest.hpp"
1920

2021
#include <stdexcept>
2122
#include <type_traits>
@@ -58,6 +59,7 @@ using iterator::Skip;
5859
using iterator::StepBy;
5960
using iterator::Take;
6061
using iterator::Zip;
62+
using iterator::ZipLongest;
6163

6264
template <class T, class Derived>
6365
class IterInterface
@@ -202,6 +204,10 @@ class IterInterface
202204
template <class Second>
203205
[[nodiscard]] auto zip(Second&& it) -> Zip<T, typename Second::Type, Derived, Second>;
204206

207+
template <class Second>
208+
[[nodiscard]] auto zipLongest(Second&& it)
209+
-> ZipLongest<T, typename Second::Type, Derived, Second>;
210+
205211
private:
206212
[[nodiscard]] inline auto self() -> Derived& { return static_cast<Derived&>(*this); }
207213
auto sizeHintChecked() -> size_t;
@@ -626,6 +632,15 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::zip(Second&& it)
626632
std::forward<Second>(it)};
627633
}
628634

635+
template <class T, class Derived>
636+
template <class Second>
637+
auto rusty_iterators::interface::IterInterface<T, Derived>::zipLongest(Second&& it)
638+
-> ZipLongest<T, typename Second::Type, Derived, Second>
639+
{
640+
return ZipLongest<T, typename Second::Type, Derived, Second>{std::forward<Derived>(self()),
641+
std::forward<Second>(it)};
642+
}
643+
629644
template <class T, class Derived>
630645
auto rusty_iterators::interface::IterInterface<T, Derived>::sizeHintChecked() -> size_t
631646
{
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#pragma once
2+
3+
#include "interface.fwd.hpp"
4+
5+
#include <algorithm>
6+
#include <optional>
7+
#include <tuple>
8+
9+
namespace rusty_iterators::iterator
10+
{
11+
using interface::IterInterface;
12+
13+
template <class T, class R, class First, class Second>
14+
class ZipLongest : public IterInterface<std::tuple<std::optional<T>, std::optional<R>>,
15+
ZipLongest<T, R, First, Second>>
16+
{
17+
public:
18+
explicit ZipLongest(First&& f, Second&& s)
19+
: first(std::forward<First>(f)), second(std::forward<Second>(s))
20+
{}
21+
22+
auto advanceBy(size_t amount) -> void;
23+
auto next() -> std::optional<std::tuple<std::optional<T>, std::optional<R>>>;
24+
[[nodiscard]] auto sizeHint() const -> std::optional<size_t>;
25+
26+
private:
27+
First first;
28+
Second second;
29+
};
30+
} // namespace rusty_iterators::iterator
31+
32+
template <class T, class R, class First, class Second>
33+
auto rusty_iterators::iterator::ZipLongest<T, R, First, Second>::advanceBy(size_t amount) -> void
34+
{
35+
first.advanceBy(amount);
36+
second.advanceBy(amount);
37+
}
38+
39+
template <class T, class R, class First, class Second>
40+
auto rusty_iterators::iterator::ZipLongest<T, R, First, Second>::next()
41+
-> std::optional<std::tuple<std::optional<T>, std::optional<R>>>
42+
{
43+
auto firstItem = first.next();
44+
auto secondItem = second.next();
45+
46+
if (!firstItem.has_value() && !secondItem.has_value())
47+
return std::nullopt;
48+
49+
return std::make_tuple(std::move(firstItem), std::move(secondItem));
50+
}
51+
52+
template <class T, class R, class First, class Second>
53+
auto rusty_iterators::iterator::ZipLongest<T, R, First, Second>::sizeHint() const
54+
-> std::optional<size_t>
55+
{
56+
auto firstSize = first.sizeHint();
57+
auto secondSize = second.sizeHint();
58+
59+
if (!firstSize.has_value() || !secondSize.has_value())
60+
return std::nullopt;
61+
62+
return std::max(firstSize.value(), secondSize.value());
63+
}

tests/zip_longest.test.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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::FieldsAre;
8+
9+
TEST(TestZipLongestIterator, TestCollectedTuples)
10+
{
11+
auto v1 = std::vector{1, 2, 3};
12+
auto v2 = std::vector{4, 5, 6};
13+
14+
auto result = LazyIterator{v1}.zipLongest(LazyIterator{v2}).collect();
15+
16+
ASSERT_EQ(result.size(), 3);
17+
EXPECT_THAT(result[0], FieldsAre(1, 4));
18+
EXPECT_THAT(result[1], FieldsAre(2, 5));
19+
EXPECT_THAT(result[2], FieldsAre(3, 6));
20+
}
21+
22+
TEST(ZipLongestIterator, TestLongerIteratorDefinesEnd)
23+
{
24+
auto v1 = std::vector{1, 2, 3};
25+
auto v2 = std::vector{4};
26+
27+
auto it = LazyIterator{v1}.zipLongest(LazyIterator{v2});
28+
29+
EXPECT_THAT(it.next().value(), FieldsAre(1, 4));
30+
EXPECT_THAT(it.next().value(), FieldsAre(2, std::nullopt));
31+
EXPECT_THAT(it.next().value(), FieldsAre(3, std::nullopt));
32+
ASSERT_EQ(it.next(), std::nullopt);
33+
}
34+
35+
TEST(ZipLongestIterator, TestSizeHintWhenOneIsInf)
36+
{
37+
auto v1 = std::vector{1, 2, 3};
38+
auto v2 = std::vector{4, 5};
39+
40+
auto it = LazyIterator{v1}.zipLongest(LazyIterator{v2}.cycle());
41+
42+
ASSERT_EQ(it.sizeHint(), std::nullopt);
43+
}
44+
45+
TEST(ZipLongestIterator, TestSizeHintWhenOneLonger)
46+
{
47+
auto v1 = std::vector{1, 2, 3};
48+
auto v2 = std::vector{1};
49+
50+
auto it = LazyIterator{v1}.zipLongest(LazyIterator{v2});
51+
52+
ASSERT_EQ(it.sizeHint(), 3);
53+
}
54+
55+
TEST(TestZipLongestIterator, TestDifferentTypesOfZippedIterators)
56+
{
57+
auto v1 = std::vector{1, 2};
58+
auto v2 = std::vector<std::string>{"a"};
59+
60+
auto it = LazyIterator{v1}.zipLongest(LazyIterator{v2});
61+
62+
auto firstItem = it.next().value();
63+
ASSERT_EQ(std::get<0>(firstItem), 1);
64+
ASSERT_EQ(std::get<1>(firstItem).value().get(), "a");
65+
66+
auto secondItem = it.next().value();
67+
ASSERT_EQ(std::get<0>(secondItem), 2);
68+
ASSERT_EQ(std::get<1>(secondItem), std::nullopt);
69+
}
70+
71+
TEST(TestZipLongestIterator, TestAdvanceBy)
72+
{
73+
auto v1 = std::vector{1, 2, 3};
74+
auto v2 = std::vector{4, 5, 6};
75+
76+
auto it = LazyIterator{v1}.zipLongest(LazyIterator{v2});
77+
78+
it.advanceBy(2);
79+
80+
EXPECT_THAT(it.next().value(), FieldsAre(3, 6));
81+
}

0 commit comments

Comments
 (0)