1
Fork 0
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:
David Laban 2016-08-02 10:29:24 +01:00
parent bb1e0540a3
commit 6751d2c708
6 changed files with 66 additions and 6 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View 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
View 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

View file

@ -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", "");