mirror of
https://github.com/nushell/nushell
synced 2024-12-27 13:33:16 +00:00
Add option to sort-by naturally (#5774)
* add `natural` option to sort-by * clippy * Add tests
This commit is contained in:
parent
4fd4136d50
commit
ff53352afe
1 changed files with 67 additions and 8 deletions
|
@ -1,3 +1,4 @@
|
|||
use alphanumeric_sort::compare_str;
|
||||
use nu_engine::{column::column_does_not_exist, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
|
@ -24,6 +25,11 @@ impl Command for SortBy {
|
|||
"Sort string-based columns case-insensitively",
|
||||
Some('i'),
|
||||
)
|
||||
.switch(
|
||||
"natural",
|
||||
"Sort alphanumeric string-based columns naturally",
|
||||
Some('n'),
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
|
||||
|
@ -73,6 +79,18 @@ impl Command for SortBy {
|
|||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
example: "[test1 test11 test2] | sort-by -n",
|
||||
description: "sort a list of alphanumeric strings naturally",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_string("test1"),
|
||||
Value::test_string("test2"),
|
||||
Value::test_string("test11"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Sort strings (case-insensitive)",
|
||||
example: "echo [airplane Truck Car] | sort-by -i",
|
||||
|
@ -131,10 +149,11 @@ impl Command for SortBy {
|
|||
let columns: Vec<String> = call.rest(engine_state, stack, 0)?;
|
||||
let reverse = call.has_flag("reverse");
|
||||
let insensitive = call.has_flag("insensitive");
|
||||
let natural = call.has_flag("natural");
|
||||
let metadata = &input.metadata();
|
||||
let mut vec: Vec<_> = input.into_iter().collect();
|
||||
|
||||
sort(&mut vec, columns, call.head, insensitive)?;
|
||||
sort(&mut vec, columns, call.head, insensitive, natural)?;
|
||||
|
||||
if reverse {
|
||||
vec.reverse()
|
||||
|
@ -155,6 +174,7 @@ pub fn sort(
|
|||
columns: Vec<String>,
|
||||
span: Span,
|
||||
insensitive: bool,
|
||||
natural: bool,
|
||||
) -> Result<(), ShellError> {
|
||||
if vec.is_empty() {
|
||||
return Err(ShellError::GenericError(
|
||||
|
@ -201,7 +221,21 @@ pub fn sort(
|
|||
.iter()
|
||||
.all(|x| matches!(x.get_type(), nu_protocol::Type::String));
|
||||
|
||||
vec.sort_by(|a, b| process(a, b, &columns, span, should_sort_case_insensitively));
|
||||
let should_sort_case_naturally = natural
|
||||
&& vals
|
||||
.iter()
|
||||
.all(|x| matches!(x.get_type(), nu_protocol::Type::String));
|
||||
|
||||
vec.sort_by(|a, b| {
|
||||
process(
|
||||
a,
|
||||
b,
|
||||
&columns,
|
||||
span,
|
||||
should_sort_case_insensitively,
|
||||
should_sort_case_naturally,
|
||||
)
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
vec.sort_by(|a, b| {
|
||||
|
@ -222,9 +256,21 @@ pub fn sort(
|
|||
_ => b.clone(),
|
||||
};
|
||||
|
||||
lowercase_left
|
||||
.partial_cmp(&lowercase_right)
|
||||
.unwrap_or(Ordering::Equal)
|
||||
if natural {
|
||||
match (lowercase_left.as_string(), lowercase_right.as_string()) {
|
||||
(Ok(left), Ok(right)) => compare_str(left, right),
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
} else {
|
||||
lowercase_left
|
||||
.partial_cmp(&lowercase_right)
|
||||
.unwrap_or(Ordering::Equal)
|
||||
}
|
||||
} else if natural {
|
||||
match (a.as_string(), b.as_string()) {
|
||||
(Ok(left), Ok(right)) => compare_str(left, right),
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
} else {
|
||||
a.partial_cmp(b).unwrap_or(Ordering::Equal)
|
||||
}
|
||||
|
@ -240,6 +286,7 @@ pub fn process(
|
|||
columns: &[String],
|
||||
span: Span,
|
||||
insensitive: bool,
|
||||
natural: bool,
|
||||
) -> Ordering {
|
||||
for column in columns {
|
||||
let left_value = left.get_data_by_key(column);
|
||||
|
@ -272,9 +319,21 @@ pub fn process(
|
|||
},
|
||||
_ => right_res,
|
||||
};
|
||||
lowercase_left
|
||||
.partial_cmp(&lowercase_right)
|
||||
.unwrap_or(Ordering::Equal)
|
||||
if natural {
|
||||
match (lowercase_left.as_string(), lowercase_right.as_string()) {
|
||||
(Ok(left), Ok(right)) => compare_str(left, right),
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
} else {
|
||||
lowercase_left
|
||||
.partial_cmp(&lowercase_right)
|
||||
.unwrap_or(Ordering::Equal)
|
||||
}
|
||||
} else if natural {
|
||||
match (left_res.as_string(), right_res.as_string()) {
|
||||
(Ok(left), Ok(right)) => compare_str(left, right),
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
} else {
|
||||
left_res.partial_cmp(&right_res).unwrap_or(Ordering::Equal)
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue