mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
implement sort --stable
Made a new function sort_by(lines, compare_fns), which accepts a list of compare_fns and calls lines.sort_by() with a closure that calls each compare_fn in turn until one returns something other than equal. Default behavior ensures that String::cmp is the last element in the compare_fns list (referred to as 'last resort' sorting by man sort). Passing --stable (-s) turns this behaviour off. Test cases provided for `sort --month` and `sort --month --stable`.
This commit is contained in:
parent
bb1e0540a3
commit
6751d2c708
6 changed files with 66 additions and 6 deletions
|
@ -43,6 +43,7 @@ struct Settings {
|
||||||
mode: SortMode,
|
mode: SortMode,
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
outfile: Option<String>,
|
outfile: Option<String>,
|
||||||
|
stable: bool,
|
||||||
unique: bool,
|
unique: bool,
|
||||||
check: bool,
|
check: bool,
|
||||||
}
|
}
|
||||||
|
@ -53,6 +54,7 @@ impl Default for Settings {
|
||||||
mode: SortMode::Default,
|
mode: SortMode::Default,
|
||||||
reverse: false,
|
reverse: false,
|
||||||
outfile: None,
|
outfile: None,
|
||||||
|
stable: false,
|
||||||
unique: false,
|
unique: false,
|
||||||
check: false,
|
check: false,
|
||||||
}
|
}
|
||||||
|
@ -70,6 +72,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
opts.optflag("h", "help", "display this help and exit");
|
opts.optflag("h", "help", "display this help and exit");
|
||||||
opts.optflag("", "version", "output version information and exit");
|
opts.optflag("", "version", "output version information and exit");
|
||||||
opts.optopt("o", "output", "write output to FILENAME instead of stdout", "FILENAME");
|
opts.optopt("o", "output", "write output to FILENAME instead of stdout", "FILENAME");
|
||||||
|
opts.optflag("s", "stable", "stabilize sort by disabling last-resort comparison");
|
||||||
opts.optflag("u", "unique", "output only the first of an equal run");
|
opts.optflag("u", "unique", "output only the first of an equal run");
|
||||||
opts.optflag("V", "version-sort", "Sort by SemVer version number, eg 1.12.2 > 1.1.2");
|
opts.optflag("V", "version-sort", "Sort by SemVer version number, eg 1.12.2 > 1.1.2");
|
||||||
opts.optflag("c", "check", "check for sorted input; do not sort");
|
opts.optflag("c", "check", "check for sorted input; do not sort");
|
||||||
|
@ -112,6 +115,7 @@ With no FILE, or when FILE is -, read standard input.", NAME, VERSION);
|
||||||
|
|
||||||
settings.reverse = matches.opt_present("reverse");
|
settings.reverse = matches.opt_present("reverse");
|
||||||
settings.outfile = matches.opt_str("output");
|
settings.outfile = matches.opt_str("output");
|
||||||
|
settings.stable = matches.opt_present("stable");
|
||||||
settings.unique = matches.opt_present("unique");
|
settings.unique = matches.opt_present("unique");
|
||||||
settings.check = matches.opt_present("check");
|
settings.check = matches.opt_present("check");
|
||||||
|
|
||||||
|
@ -146,14 +150,25 @@ fn exec(files: Vec<String>, settings: &Settings) -> i32 {
|
||||||
|
|
||||||
let original_lines = lines.to_vec();
|
let original_lines = lines.to_vec();
|
||||||
|
|
||||||
match settings.mode {
|
let mut compare_fns = Vec::new();
|
||||||
SortMode::Numeric => lines.sort_by(numeric_compare),
|
|
||||||
SortMode::HumanNumeric => lines.sort_by(human_numeric_size_compare),
|
compare_fns.push(match settings.mode {
|
||||||
SortMode::Month => lines.sort_by(month_compare),
|
SortMode::Numeric => numeric_compare,
|
||||||
SortMode::Version => lines.sort_by(version_compare),
|
SortMode::HumanNumeric => human_numeric_size_compare,
|
||||||
SortMode::Default => lines.sort()
|
SortMode::Month => month_compare,
|
||||||
|
SortMode::Version => version_compare,
|
||||||
|
SortMode::Default => String::cmp
|
||||||
|
});
|
||||||
|
|
||||||
|
if !settings.stable {
|
||||||
|
match settings.mode {
|
||||||
|
SortMode::Default => {}
|
||||||
|
_ => compare_fns.push(String::cmp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort_by(&mut lines, compare_fns);
|
||||||
|
|
||||||
if settings.unique {
|
if settings.unique {
|
||||||
lines.dedup()
|
lines.dedup()
|
||||||
}
|
}
|
||||||
|
@ -178,6 +193,20 @@ fn exec(files: Vec<String>, settings: &Settings) -> i32 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sort_by<F>(lines: &mut Vec<String>, compare_fns: Vec<F>)
|
||||||
|
where F: Fn( &String, &String ) -> Ordering
|
||||||
|
{
|
||||||
|
lines.sort_by(|a, b| {
|
||||||
|
for compare_fn in &compare_fns {
|
||||||
|
let cmp = compare_fn(a, b);
|
||||||
|
if cmp != Ordering::Equal {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ordering::Equal;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse the beginning string into an f64, returning -inf instead of NaN on errors.
|
/// Parse the beginning string into an f64, returning -inf instead of NaN on errors.
|
||||||
fn permissive_f64_parse(a: &str) -> f64 {
|
fn permissive_f64_parse(a: &str) -> f64 {
|
||||||
// Maybe should be split on non-digit, but then 10e100 won't parse properly.
|
// Maybe should be split on non-digit, but then 10e100 won't parse properly.
|
||||||
|
|
3
tests/fixtures/sort/month_default.expected
vendored
3
tests/fixtures/sort/month_default.expected
vendored
|
@ -3,5 +3,8 @@ Jan Lorem ipsum dolor sit amet
|
||||||
mar laboris nisi ut aliquip ex ea
|
mar laboris nisi ut aliquip ex ea
|
||||||
May sed do eiusmod tempor incididunt
|
May sed do eiusmod tempor incididunt
|
||||||
JUN nostrud exercitation ullamco
|
JUN nostrud exercitation ullamco
|
||||||
|
Jul 1 should remain 2,1,3
|
||||||
|
Jul 2 these three lines
|
||||||
|
Jul 3 if --stable is provided
|
||||||
Oct ut labore et dolore magna aliqua
|
Oct ut labore et dolore magna aliqua
|
||||||
Dec consectetur adipiscing elit
|
Dec consectetur adipiscing elit
|
||||||
|
|
3
tests/fixtures/sort/month_default.txt
vendored
3
tests/fixtures/sort/month_default.txt
vendored
|
@ -5,3 +5,6 @@ Oct ut labore et dolore magna aliqua
|
||||||
N/A Ut enim ad minim veniam, quis
|
N/A Ut enim ad minim veniam, quis
|
||||||
JUN nostrud exercitation ullamco
|
JUN nostrud exercitation ullamco
|
||||||
mar laboris nisi ut aliquip ex ea
|
mar laboris nisi ut aliquip ex ea
|
||||||
|
Jul 2 these three lines
|
||||||
|
Jul 1 should remain 2,1,3
|
||||||
|
Jul 3 if --stable is provided
|
||||||
|
|
10
tests/fixtures/sort/month_stable.expected
vendored
Normal file
10
tests/fixtures/sort/month_stable.expected
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
N/A Ut enim ad minim veniam, quis
|
||||||
|
Jan Lorem ipsum dolor sit amet
|
||||||
|
mar laboris nisi ut aliquip ex ea
|
||||||
|
May sed do eiusmod tempor incididunt
|
||||||
|
JUN nostrud exercitation ullamco
|
||||||
|
Jul 2 these three lines
|
||||||
|
Jul 1 should remain 2,1,3
|
||||||
|
Jul 3 if --stable is provided
|
||||||
|
Oct ut labore et dolore magna aliqua
|
||||||
|
Dec consectetur adipiscing elit
|
10
tests/fixtures/sort/month_stable.txt
vendored
Normal file
10
tests/fixtures/sort/month_stable.txt
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Jan Lorem ipsum dolor sit amet
|
||||||
|
Dec consectetur adipiscing elit
|
||||||
|
May sed do eiusmod tempor incididunt
|
||||||
|
Oct ut labore et dolore magna aliqua
|
||||||
|
N/A Ut enim ad minim veniam, quis
|
||||||
|
JUN nostrud exercitation ullamco
|
||||||
|
mar laboris nisi ut aliquip ex ea
|
||||||
|
Jul 2 these three lines
|
||||||
|
Jul 1 should remain 2,1,3
|
||||||
|
Jul 3 if --stable is provided
|
|
@ -37,6 +37,11 @@ fn test_month_default() {
|
||||||
test_helper("month_default", "-M");
|
test_helper("month_default", "-M");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_month_stable() {
|
||||||
|
test_helper("month_stable", "-Ms");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_unsorted_ints() {
|
fn test_default_unsorted_ints() {
|
||||||
test_helper("default_unsorted_ints", "");
|
test_helper("default_unsorted_ints", "");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue