mirror of
https://github.com/nushell/nushell
synced 2024-12-26 21:13:19 +00:00
add case-insensitive sorting (#919)
This commit is contained in:
parent
e1c28cf06b
commit
3d3298290a
1 changed files with 91 additions and 13 deletions
|
@ -1,11 +1,10 @@
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use nu_engine::column::column_does_not_exist;
|
use nu_engine::{column::column_does_not_exist, CallExt};
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
ast::Call,
|
||||||
SyntaxShape, Value,
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Config, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature,
|
||||||
|
Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
@ -21,6 +20,11 @@ impl Command for SortBy {
|
||||||
Signature::build("sort-by")
|
Signature::build("sort-by")
|
||||||
.rest("columns", SyntaxShape::Any, "the column(s) to sort by")
|
.rest("columns", SyntaxShape::Any, "the column(s) to sort by")
|
||||||
.switch("reverse", "Sort in reverse order", Some('r'))
|
.switch("reverse", "Sort in reverse order", Some('r'))
|
||||||
|
.switch(
|
||||||
|
"insensitive",
|
||||||
|
"Sort string-based columns case-insensitively",
|
||||||
|
Some('i'),
|
||||||
|
)
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +74,30 @@ impl Command for SortBy {
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Sort strings (case-insensitive)",
|
||||||
|
example: "echo [airplane Truck Car] | sort-by -i",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::test_string("airplane"),
|
||||||
|
Value::test_string("Car"),
|
||||||
|
Value::test_string("Truck"),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Sort strings (reversed case-insensitive)",
|
||||||
|
example: "echo [airplane Truck Car] | sort-by -i -r",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::test_string("Truck"),
|
||||||
|
Value::test_string("Car"),
|
||||||
|
Value::test_string("airplane"),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,10 +110,12 @@ impl Command for SortBy {
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let columns: Vec<String> = call.rest(engine_state, stack, 0)?;
|
let columns: Vec<String> = call.rest(engine_state, stack, 0)?;
|
||||||
let reverse = call.has_flag("reverse");
|
let reverse = call.has_flag("reverse");
|
||||||
|
let insensitive = call.has_flag("insensitive");
|
||||||
let metadata = &input.metadata();
|
let metadata = &input.metadata();
|
||||||
|
let config = stack.get_config()?;
|
||||||
let mut vec: Vec<_> = input.into_iter().collect();
|
let mut vec: Vec<_> = input.into_iter().collect();
|
||||||
|
|
||||||
sort(&mut vec, columns, call)?;
|
sort(&mut vec, columns, call, insensitive, &config)?;
|
||||||
|
|
||||||
if reverse {
|
if reverse {
|
||||||
vec.reverse()
|
vec.reverse()
|
||||||
|
@ -101,7 +131,18 @@ impl Command for SortBy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sort(vec: &mut [Value], columns: Vec<String>, call: &Call) -> Result<(), ShellError> {
|
pub fn sort(
|
||||||
|
vec: &mut [Value],
|
||||||
|
columns: Vec<String>,
|
||||||
|
call: &Call,
|
||||||
|
insensitive: bool,
|
||||||
|
config: &Config,
|
||||||
|
) -> Result<(), ShellError> {
|
||||||
|
let should_sort_case_insensitively = insensitive
|
||||||
|
&& vec
|
||||||
|
.iter()
|
||||||
|
.all(|x| matches!(x.get_type(), nu_protocol::Type::String));
|
||||||
|
|
||||||
match &vec[0] {
|
match &vec[0] {
|
||||||
Value::Record {
|
Value::Record {
|
||||||
cols,
|
cols,
|
||||||
|
@ -118,13 +159,36 @@ pub fn sort(vec: &mut [Value], columns: Vec<String>, call: &Call) -> Result<(),
|
||||||
}
|
}
|
||||||
|
|
||||||
vec.sort_by(|a, b| {
|
vec.sort_by(|a, b| {
|
||||||
process(a, b, &columns[0], call)
|
process(
|
||||||
.expect("sort_by Value::Record bug")
|
a,
|
||||||
.compare()
|
b,
|
||||||
|
&columns[0],
|
||||||
|
call,
|
||||||
|
should_sort_case_insensitively,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
.expect("sort_by Value::Record bug")
|
||||||
|
.compare()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
vec.sort_by(|a, b| coerce_compare(a, b).expect("sort_by default bug").compare());
|
vec.sort_by(|a, b| {
|
||||||
|
if should_sort_case_insensitively {
|
||||||
|
let lowercase_left = Value::string(
|
||||||
|
a.into_string("", config).to_ascii_lowercase(),
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
let lowercase_right = Value::string(
|
||||||
|
b.into_string("", config).to_ascii_lowercase(),
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
coerce_compare(&lowercase_left, &lowercase_right)
|
||||||
|
.expect("sort_by default bug")
|
||||||
|
.compare()
|
||||||
|
} else {
|
||||||
|
coerce_compare(a, b).expect("sort_by default bug").compare()
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -135,6 +199,8 @@ pub fn process(
|
||||||
right: &Value,
|
right: &Value,
|
||||||
column: &str,
|
column: &str,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
|
insensitive: bool,
|
||||||
|
config: &Config,
|
||||||
) -> Result<CompareValues, (&'static str, &'static str)> {
|
) -> Result<CompareValues, (&'static str, &'static str)> {
|
||||||
let left_value = left.get_data_by_key(column);
|
let left_value = left.get_data_by_key(column);
|
||||||
|
|
||||||
|
@ -150,7 +216,19 @@ pub fn process(
|
||||||
None => Value::Nothing { span: call.head },
|
None => Value::Nothing { span: call.head },
|
||||||
};
|
};
|
||||||
|
|
||||||
coerce_compare(&left_res, &right_res)
|
if insensitive {
|
||||||
|
let lowercase_left = Value::string(
|
||||||
|
left_res.into_string("", config).to_ascii_lowercase(),
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
let lowercase_right = Value::string(
|
||||||
|
right_res.into_string("", config).to_ascii_lowercase(),
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
coerce_compare(&lowercase_left, &lowercase_right)
|
||||||
|
} else {
|
||||||
|
coerce_compare(&left_res, &right_res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
Loading…
Reference in a new issue