Skip to content

Commit d304eb8

Browse files
committed
✨ Add min_by_key, max_by, min_by and clean up by_key implementation
1 parent fe2e2c9 commit d304eb8

5 files changed

Lines changed: 78 additions & 19 deletions

File tree

ndc_lib/src/stdlib/sequence.rs

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,29 @@ mod inner {
112112
}
113113
}
114114
pub fn max_by_key(seq: &mut Sequence, func: &Callable<'_>) -> EvaluationResult {
115-
let mut best_value = None;
116-
let mut best_key = None;
117-
118-
for value in mut_seq_to_iterator(seq) {
119-
if let Some(best_key_val) = &best_key {
120-
let new_key = func.call(&mut [value.clone()])?;
121-
if &new_key > best_key_val {
122-
best_key = Some(new_key);
123-
best_value = Some(value);
124-
}
125-
} else {
126-
best_key = Some(func.call(&mut [value.clone()])?);
127-
best_value = Some(value);
128-
}
129-
}
115+
by_key(seq, func, Ordering::Greater)
116+
}
130117

131-
match best_value {
132-
None => Err(anyhow::anyhow!("empty input to max_by_key"))?,
133-
Some(value) => Ok(value),
134-
}
118+
pub fn min_by_key(seq: &mut Sequence, func: &Callable<'_>) -> EvaluationResult {
119+
by_key(seq, func, Ordering::Less)
120+
}
121+
122+
/// Returns the maximum element using a comparator function.
123+
///
124+
/// The comparator function takes two elements and returns a number. A positive result means the
125+
/// first argument is greater than the second, a negative result means the first argument is
126+
/// less than the second, and zero means they are equal.
127+
pub fn max_by(seq: &mut Sequence, comp: &Callable<'_>) -> EvaluationResult {
128+
by_comp(seq, comp, Ordering::Greater)
129+
}
130+
131+
/// Returns the minimum element using a comparator function.
132+
///
133+
/// The comparator function takes two elements and returns a number. A positive result means the
134+
/// first argument is greater than the second, a negative result means the first argument is
135+
/// less than the second, and zero means they are equal.
136+
pub fn min_by(seq: &mut Sequence, comp: &Callable<'_>) -> EvaluationResult {
137+
by_comp(seq, comp, Ordering::Less)
135138
}
136139

137140
/// Returns the lowest element in the sequence.
@@ -772,6 +775,52 @@ mod inner {
772775
}
773776
}
774777

778+
fn by_key(
779+
seq: &mut Sequence,
780+
func: &Callable<'_>,
781+
better: Ordering,
782+
) -> EvaluationResult {
783+
let mut best_value = None;
784+
let mut best_key: Option<Value> = None;
785+
786+
for value in mut_seq_to_iterator(seq) {
787+
let new_key = func.call(&mut [value.clone()])?;
788+
let is_better = match &best_key {
789+
None => true,
790+
Some(current_best) => new_key.try_cmp(current_best)? == better,
791+
};
792+
if is_better {
793+
best_key = Some(new_key);
794+
best_value = Some(value);
795+
}
796+
}
797+
798+
best_value.ok_or_else(|| anyhow::anyhow!("sequence was empty").into())
799+
}
800+
801+
fn by_comp(
802+
seq: &mut Sequence,
803+
comp: &Callable<'_>,
804+
better: Ordering,
805+
) -> EvaluationResult {
806+
let mut best: Option<Value> = None;
807+
808+
for value in mut_seq_to_iterator(seq) {
809+
let is_better = match &best {
810+
None => true,
811+
Some(current) => {
812+
let result = comp.call(&mut [value.clone(), current.clone()])?;
813+
result.try_cmp(&Value::from(0))? == better
814+
}
815+
};
816+
if is_better {
817+
best = Some(value);
818+
}
819+
}
820+
821+
best.ok_or_else(|| anyhow::anyhow!("sequence was empty").into())
822+
}
823+
775824
fn fold_iterator(
776825
iterator: MutableValueIntoIterator<'_>,
777826
initial: Value,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
assert_eq([3, 1, 4, 1, 5, 9].max_by_key(fn(x) => x), 9);
2+
assert_eq(["foo", "ba", "quux"].max_by_key(fn(s) => s.len()), "quux");
3+
assert_eq([(1, 10), (2, 5), (3, 8)].max_by_key(fn(t) => t[1]), (1, 10));
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
assert_eq([3, 1, 4, 1, 5, 9].min_by_key(fn(x) => x), 1);
2+
assert_eq(["foo", "ba", "quux"].min_by_key(fn(s) => s.len()), "ba");
3+
assert_eq([(1, 10), (2, 5), (3, 8)].min_by_key(fn(t) => t[1]), (2, 5));
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
assert_eq([3, 1, 4, 1, 5, 9].max_by(fn(a, b) => a <=> b), 9);
2+
assert_eq(["foo", "ba", "quux"].max_by(fn(a, b) => a.len() <=> b.len()), "quux");
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
assert_eq([3, 1, 4, 1, 5, 9].min_by(fn(a, b) => a <=> b), 1);
2+
assert_eq(["foo", "ba", "quux"].min_by(fn(a, b) => a.len() <=> b.len()), "ba");

0 commit comments

Comments
 (0)