Skip to content

Commit 6c585c7

Browse files
committed
Implement unique as Iterator
Similar in spirit to https://docs.rs/itertools/latest/src/itertools/unique_impl.rs.html but ofc less sophisticated
1 parent 18cc3c2 commit 6c585c7

File tree

1 file changed

+116
-23
lines changed

1 file changed

+116
-23
lines changed

src/day02.rs

Lines changed: 116 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::{HashMap, HashSet};
22
use std::fs;
33
use std::hash::Hash;
44

@@ -43,32 +43,33 @@ fn is_safe_part1(report: &Vec<i32>) -> bool {
4343
.all(|x| x < 4);
4444

4545
let is_strictly_monotonic = 1
46-
== unique(
47-
report
48-
.windows(2)
49-
.map(|pair| {
50-
let [a, b] = pair else { unreachable!() };
51-
i32::signum(a - b)
52-
})
53-
.collect(),
54-
)
55-
.len();
56-
steps_small_enough && is_strictly_monotonic
57-
}
46+
== report
47+
.windows(2)
48+
.map(|pair| {
49+
let [a, b] = pair else { unreachable!() };
50+
i32::signum(a - b)
51+
})
52+
.uniques()
53+
.count();
54+
// instead of
55+
// let is_strictly_monotonic = 1
56+
// == unique(
57+
// report
58+
// .windows(2)
59+
// .map(|pair| {
60+
// let [a, b] = pair else { unreachable!() };
61+
// i32::signum(a - b)
62+
// })
63+
// .collect(),
64+
// )
65+
// .len();
5866

59-
fn unique<T>(v: Vec<T>) -> Vec<T>
60-
where
61-
T: Eq + Hash,
62-
{
63-
v.into_iter()
64-
.collect::<std::collections::HashSet<T>>()
65-
.into_iter()
66-
.collect()
67+
steps_small_enough && is_strictly_monotonic
6768
}
6869

6970
fn is_safe_part2(mut report: Vec<i32>) -> bool {
70-
if is_safe_part2_(report.clone()){
71-
return true;
71+
if is_safe_part2_(report.clone()) {
72+
return true;
7273
}
7374
report.reverse();
7475
is_safe_part2_(report)
@@ -124,3 +125,95 @@ where
124125
condition(a, b)
125126
})
126127
}
128+
129+
#[allow(dead_code)]
130+
fn unique<T>(v: Vec<T>) -> Vec<T>
131+
where
132+
T: Eq + Hash,
133+
{
134+
v.into_iter()
135+
.collect::<std::collections::HashSet<T>>()
136+
.into_iter()
137+
.collect()
138+
}
139+
140+
141+
// upside: can be chained nicely, is lazy
142+
// downside: need clonable, up to 2x memory requirements
143+
struct Uniques<I, T>
144+
where
145+
T: Eq + Hash + Clone,
146+
I: Iterator<Item = T>,
147+
{
148+
input: I,
149+
seen: HashSet<T>,
150+
}
151+
152+
impl<I, T> Uniques<I, T>
153+
where
154+
T: Eq + Hash + Clone,
155+
I: Iterator<Item = T>,
156+
{
157+
fn new(input: I) -> Self {
158+
Self {
159+
input,
160+
seen: HashSet::new(),
161+
}
162+
}
163+
}
164+
165+
impl<I, T> Iterator for Uniques<I, T>
166+
where
167+
T: Eq + Hash + Clone,
168+
I: Iterator<Item = T>,
169+
{
170+
type Item = T;
171+
172+
fn next(&mut self) -> Option<Self::Item> {
173+
while let Some(item) = self.input.next() {
174+
if self.seen.insert(item.clone()) {
175+
return Some(item);
176+
}
177+
}
178+
None
179+
}
180+
}
181+
182+
// Extension trait to make it convenient to use
183+
trait IteratorExt: Iterator {
184+
fn uniques(self) -> Uniques<Self, Self::Item>
185+
where
186+
Self: Sized,
187+
Self::Item: Eq + Hash + Clone,
188+
{
189+
Uniques::new(self)
190+
}
191+
}
192+
193+
impl<I: Iterator> IteratorExt for I {}
194+
195+
#[cfg(test)]
196+
mod tests {
197+
use super::*;
198+
199+
#[test]
200+
fn test_uniques() {
201+
let input = vec![1, 2, 3, 2, 4, 1, 5];
202+
let result: Vec<i32> = input.into_iter().uniques().collect();
203+
assert_eq!(result, vec![1, 2, 3, 4, 5]);
204+
}
205+
206+
#[test]
207+
fn test_uniques_strings() {
208+
let input = vec!["a", "b", "c", "b", "a", "d"];
209+
let result: Vec<&str> = input.into_iter().uniques().collect();
210+
assert_eq!(result, vec!["a", "b", "c", "d"]);
211+
}
212+
213+
#[test]
214+
fn test_empty() {
215+
let input: Vec<i32> = vec![];
216+
let result: Vec<i32> = input.into_iter().uniques().collect();
217+
assert_eq!(result, vec![]);
218+
}
219+
}

0 commit comments

Comments
 (0)