Follow-ups from the review that produced #17. These are suggestions, not bugs — each is independent and up for discussion.
1. value_counts: use Counter instead of sort + groupby
The current implementation sorts the input, so it:
- fails with
TypeError on hashable-but-unorderable items, e.g. Itr([1j, 1j, 2j]).value_counts() — even though collect(dict) only requires hashability;
- costs O(n log n) where counting is O(n).
Itr(Counter(self._it).items()) fixes both. Caveat: output order changes from sorted-key to first-seen, and the sorted-key ordering is explicitly documented in the 0.3.0 release notes — so this is a (minor) breaking change and needs a deliberate decision.
2. peek(): one-item lookahead buffer instead of tee
Each peek() wraps _it in another itertools.tee layer, so repeated peeks build an ever-deeper chain of tees. A single-item lookahead buffer (like Rust's Peekable) would make peek O(1) regardless of call count, and opens the door to a next_if(predicate) API.
3. Missing Rust-trait staples
Natural additions given the library's stated inspiration:
sum() / prod() — terminal aggregations (thin wrappers over sum / math.prod);
dedup() — lazy removal of adjacent duplicates (pairs nicely with chunk_by, works on infinite iterators);
zip_longest(other, fillvalue=...) — the existing zip truncates to the shorter input;
sorted_by(key) — eager terminal, complementing groupby's sort-then-group.
4. repeat(1) aliasing inconsistency
repeat(1) returns self, while every other path returns a new Itr (and repeat(n>1) leaves the original exhausted via tee). Harmless, but consuming the returned iterator mutates the original in the n == 1 case only. Dropping the special case would make behaviour uniform.
🤖 Generated with Claude Code
Follow-ups from the review that produced #17. These are suggestions, not bugs — each is independent and up for discussion.
1.
value_counts: useCounterinstead of sort +groupbyThe current implementation sorts the input, so it:
TypeErroron hashable-but-unorderable items, e.g.Itr([1j, 1j, 2j]).value_counts()— even thoughcollect(dict)only requires hashability;Itr(Counter(self._it).items())fixes both. Caveat: output order changes from sorted-key to first-seen, and the sorted-key ordering is explicitly documented in the 0.3.0 release notes — so this is a (minor) breaking change and needs a deliberate decision.2.
peek(): one-item lookahead buffer instead ofteeEach
peek()wraps_itin anotheritertools.teelayer, so repeated peeks build an ever-deeper chain of tees. A single-item lookahead buffer (like Rust'sPeekable) would makepeekO(1) regardless of call count, and opens the door to anext_if(predicate)API.3. Missing Rust-trait staples
Natural additions given the library's stated inspiration:
sum()/prod()— terminal aggregations (thin wrappers oversum/math.prod);dedup()— lazy removal of adjacent duplicates (pairs nicely withchunk_by, works on infinite iterators);zip_longest(other, fillvalue=...)— the existingziptruncates to the shorter input;sorted_by(key)— eager terminal, complementinggroupby's sort-then-group.4.
repeat(1)aliasing inconsistencyrepeat(1)returnsself, while every other path returns a newItr(andrepeat(n>1)leaves the original exhausted viatee). Harmless, but consuming the returned iterator mutates the original in then == 1case only. Dropping the special case would make behaviour uniform.🤖 Generated with Claude Code