Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions benchmarks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
benchmark_more_itertools_flatten,
benchmark_more_itertools_nth,
benchmark_more_itertools_take,
benchmark_more_itertools_unique,
benchmark_more_itertools_unzip,
benchmark_rusty_iterators_cache_cycle,
benchmark_rusty_iterators_copy_cycle,
Expand All @@ -17,6 +18,7 @@
benchmark_rusty_iterators_nth,
benchmark_rusty_iterators_sequence_wrapper,
benchmark_rusty_iterators_take,
benchmark_rusty_iterators_unique,
benchmark_rusty_iterators_unzip,
benchmark_stdlib_count,
benchmark_stdlib_filter,
Expand All @@ -29,6 +31,7 @@
"benchmark_more_itertools_flatten",
"benchmark_more_itertools_nth",
"benchmark_more_itertools_take",
"benchmark_more_itertools_unique",
"benchmark_more_itertools_unzip",
"benchmark_rusty_iterators_cache_cycle",
"benchmark_rusty_iterators_copy_cycle",
Expand All @@ -40,6 +43,7 @@
"benchmark_rusty_iterators_nth",
"benchmark_rusty_iterators_sequence_wrapper",
"benchmark_rusty_iterators_take",
"benchmark_rusty_iterators_unique",
"benchmark_rusty_iterators_unzip",
"benchmark_stdlib_count",
"benchmark_stdlib_filter",
Expand Down
10 changes: 10 additions & 0 deletions benchmarks/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,13 @@ def benchmark_more_itertools_take(arg: Iterable[int]) -> None:
@BenchmarkManager.register(arg=range(1_000_000))
def benchmark_rusty_iterators_take(arg: Iterable[int]) -> None:
_ = LIter.from_it(iter(arg)).take(500_000).collect()


@BenchmarkManager.register(arg=range(1_000_000))
def benchmark_rusty_iterators_unique(arg: Iterable[int]) -> None:
_ = LIter.from_it(iter(arg)).unique().collect()


@BenchmarkManager.register(arg=range(1_000_000))
def benchmark_more_itertools_unique(arg: Iterable[int]) -> None:
_ = list(mi.unique(arg))
5 changes: 5 additions & 0 deletions rusty_iterators/core/interface.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ cdef class IterInterface:
cpdef StepBy step_by(self, int step)
cpdef object sum(self)
cpdef Take take(self, int amount)
cpdef Unique unique(self)
cpdef object unzip(self)
cpdef Zip zip(self, IterInterface second)

Expand Down Expand Up @@ -93,6 +94,10 @@ cdef class Take(IterInterface):
cdef int taken
cdef int amount

cdef class Unique(IterInterface):
cdef IterInterface it
cdef set used

cdef class Zip(IterInterface):
cdef IterInterface first
cdef IterInterface second
Expand Down
5 changes: 5 additions & 0 deletions rusty_iterators/core/interface.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class IterInterface(Generic[T]):
def step_by(self, step: int) -> StepBy[T]: ...
def sum(self: IterInterface[T_addable]) -> T_addable: ...
def take(self, amount: int) -> Take[T]: ...
def unique(self) -> Unique[T]: ...
@overload
def unzip(self: IterInterface[list[R]]) -> tuple[list[R], list[R]]: ...
@overload
Expand Down Expand Up @@ -147,3 +148,7 @@ class Take(IterInterface[T]):
@final
class Zip(IterInterface[ZipItem[T, R]]):
def __init__(self, first: IterInterface[T], second: IterInterface[R]) -> None: ...

@final
class Unique(IterInterface[T]):
def __init__(self, it: IterInterface[T]) -> None: ...
30 changes: 30 additions & 0 deletions rusty_iterators/core/interface.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ cdef class IterInterface:
cpdef Take take(self, int amount):
return Take(self, amount)

cpdef Unique unique(self):
return Unique(self)

cpdef object unzip(self):
cdef list left = []
cdef list right = []
Expand Down Expand Up @@ -571,3 +574,30 @@ cdef class Zip(IterInterface):

cpdef object next(self):
return (self.first.next(), self.second.next())

@cython.final
cdef class Unique(IterInterface):
def __cinit__(self, IterInterface it):
self.it = it
self.used = set()

def __str__(self):
return f"Unique(it={self.it})"

cpdef Unique copy(self):
cdef Unique obj

obj = Unique(self.it.copy())
obj.used = self.used.copy()

return obj

cpdef object next(self):
cdef object item

while True:
item = self.it.next()

if item not in self.used:
self.used.add(item)
return item
9 changes: 9 additions & 0 deletions tests/types/verify_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
Skip,
StepBy,
Take,
Unique,
Zip,
)
from rusty_iterators.core.wrappers import IterWrapper, SeqWrapper
Expand Down Expand Up @@ -234,6 +235,14 @@ def verify_inspect_type() -> None:
assert_type(it.next(), int)


def verify_unique_type() -> None:
it = LIter.from_items(1, 2, 3).unique()

assert_type(it, Unique[int])
assert_type(it.next(), int)
assert_type(it.collect(), list[int])


def verify_peekable_type() -> None:
it = LIter.from_items(1, 2, 3).peekable()

Expand Down
21 changes: 21 additions & 0 deletions tests/unit/sync/test_unique.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from rusty_iterators import LIter


def test_unique_values() -> None:
it = LIter.from_items(1, 2, 1, 3, 4, 2, 5, 1, 2)

assert it.unique().collect() == [1, 2, 3, 4, 5]


def test_unique_copy() -> None:
it = LIter.from_items(1, 2, 1, 3, 4, 2, 5, 1, 2).unique()
cp = it.copy()

it.next()

assert it.collect() == [2, 3, 4, 5]
assert cp.collect() == [1, 2, 3, 4, 5]


def test_unique_empty_iterator() -> None:
assert LIter.from_items().unique().collect() == []