Add compare argument for array.sorted

This commit is contained in:
+merlan #flirora 2024-11-04 15:17:36 -05:00 committed by Malo
parent bb38a01d06
commit c7216c26f5
2 changed files with 31 additions and 18 deletions

View File

@ -841,31 +841,42 @@ impl Array {
/// determine the keys to sort by. /// determine the keys to sort by.
#[named] #[named]
key: Option<Func>, key: Option<Func>,
/// If given, uses this function to compare elements in the array to
/// determine their relative order.
#[named]
compare: Option<Func>,
) -> SourceResult<Array> { ) -> SourceResult<Array> {
let mut result = Ok(()); let mut result = Ok(());
let mut vec = self.0; let mut vec = self.0;
let mut key_of = |x: Value| match &key { let mut compare = |x: Value, y: Value| {
// NOTE: We are relying on `comemo`'s memoization of function let mut key_of = |x: Value| match &key {
// evaluation to not excessively reevaluate the `key`. // NOTE: We are relying on `comemo`'s memoization of function
Some(f) => f.call(engine, context, [x]), // evaluation to not excessively reevaluate the `key`.
None => Ok(x), Some(f) => f.call(engine, context, [x]),
None => Ok(x),
};
let x = key_of(x)?;
let y = key_of(y)?;
match &compare {
Some(f) => Ok(match f.call(engine, context, [x, y])? {
Value::Int(x) => x.cmp(&0),
x => bail!(
span,
"expected integer from `compare` function; got {}",
x.repr()
),
}),
None => ops::compare(&x, &y).at(span),
}
}; };
vec.make_mut().sort_by(|a, b| { vec.make_mut().sort_by(|a, b| {
// Until we get `try` blocks :) // Until we get `try` blocks :)
match (key_of(a.clone()), key_of(b.clone())) { compare(a.clone(), b.clone()).unwrap_or_else(|err| {
(Ok(a), Ok(b)) => ops::compare(&a, &b).unwrap_or_else(|err| { if result.is_ok() {
if result.is_ok() { result = Err(err);
result = Err(err).at(span);
}
Ordering::Equal
}),
(Err(e), _) | (_, Err(e)) => {
if result.is_ok() {
result = Err(e);
}
Ordering::Equal
} }
} Ordering::Equal
})
}); });
result.map(|_| vec.into()) result.map(|_| vec.into())
} }

View File

@ -355,6 +355,8 @@
#test((2, 1, 3, 10, 5, 8, 6, -7, 2).sorted(), (-7, 1, 2, 2, 3, 5, 6, 8, 10)) #test((2, 1, 3, 10, 5, 8, 6, -7, 2).sorted(), (-7, 1, 2, 2, 3, 5, 6, 8, 10))
#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x), (-10, -7, -5, 1, 2, 2, 3, 6, 8)) #test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x), (-10, -7, -5, 1, 2, 2, 3, 6, 8))
#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x * x), (1, 2, 2, 3, -5, 6, -7, 8, -10)) #test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x * x), (1, 2, 2, 3, -5, 6, -7, 8, -10))
#test(("I", "the", "hi", "text").sorted(compare: (x, y) => x.len() - y.len()), ("I", "hi", "the", "text"))
#test(("I", "the", "hi", "text").sorted(key: x => x.len(), compare: (x, y) => y - x), ("text", "the", "hi", "I"))
--- array-sorted-key-function-positional-1 --- --- array-sorted-key-function-positional-1 ---
// Error: 12-18 unexpected argument // Error: 12-18 unexpected argument