diff --git a/src/cmd/sort.rs b/src/cmd/sort.rs index 647fb3c2..ca0a1429 100644 --- a/src/cmd/sort.rs +++ b/src/cmd/sort.rs @@ -6,6 +6,8 @@ use select::SelectColumns; use util; use std::str::from_utf8; +use self::Number::{Float, Int}; + static USAGE: &'static str = " Sorts CSV data lexicographically. @@ -116,7 +118,7 @@ pub fn iter_cmp_num<'a, L, R>(mut a: L, mut b: R) -> cmp::Ordering (None, None) => return cmp::Ordering::Equal, (None, _ ) => return cmp::Ordering::Less, (_ , None) => return cmp::Ordering::Greater, - (Some(x), Some(y)) => match x.cmp(&y) { + (Some(x), Some(y)) => match compare_num(x, y) { cmp::Ordering::Equal => (), non_eq => return non_eq, }, @@ -124,9 +126,34 @@ pub fn iter_cmp_num<'a, L, R>(mut a: L, mut b: R) -> cmp::Ordering } } -fn next_num<'a, X>(xs: &mut X) -> Option +#[derive(Clone, Copy, PartialEq)] +enum Number { + Int(i64), + Float(f64), +} + +fn compare_num(n1: Number, n2: Number) -> cmp::Ordering{ + match (n1, n2) { + (Int(i1), Int(i2)) => i1.cmp(&i2), + (Int(i1), Float(f2)) => compare_float(i1 as f64, f2), + (Float(f1), Int(i2)) => compare_float(f1, i2 as f64), + (Float(f1), Float(f2)) => compare_float(f1, f2), + } +} + +fn compare_float(f1: f64, f2: f64) -> cmp::Ordering { + f1.partial_cmp(&f2).unwrap_or(cmp::Ordering::Equal) +} + + + +fn next_num<'a, X>(xs: &mut X) -> Option where X: Iterator { xs.next() .and_then(|bytes| from_utf8(bytes).ok()) - .and_then(|s| s.parse::().ok()) + .and_then(|s| { + if let Ok(i) = s.parse::() { Some(Number::Int(i)) } + else if let Ok(f) = s.parse::() { Some(Number::Float(f)) } + else { None } + }) } diff --git a/tests/test_sort.rs b/tests/test_sort.rs index 8e3bc444..7ece56c6 100644 --- a/tests/test_sort.rs +++ b/tests/test_sort.rs @@ -59,8 +59,9 @@ fn sort_numeric() { wrk.create("in.csv", vec![ svec!["N", "S"], svec!["10", "a"], + svec!["LETTER", "b"], svec!["2", "c"], - svec!["1", "b"], + svec!["1", "d"], ]); let mut cmd = wrk.command("sort"); @@ -69,13 +70,43 @@ fn sort_numeric() { let got: Vec> = wrk.read_stdout(&mut cmd); let expected = vec![ svec!["N", "S"], - svec!["1", "b"], + //Non-numerics should be put first + svec!["LETTER", "b"], + svec!["1", "d"], svec!["2", "c"], svec!["10", "a"], ]; assert_eq!(got, expected); } +#[test] +fn sort_numeric_non_natural() { + let wrk = Workdir::new("sort_numeric_non_natural"); + wrk.create("in.csv", vec![ + svec!["N", "S"], + svec!["8.33", "a"], + svec!["5", "b"], + svec!["LETTER", "c"], + svec!["7.4", "d"], + svec!["3.33", "e"], + ]); + + let mut cmd = wrk.command("sort"); + cmd.arg("-N").arg("in.csv"); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec!["N", "S"], + //Non-numerics should be put first + svec!["LETTER", "c"], + svec!["3.33", "e"], + svec!["5", "b"], + svec!["7.4", "d"], + svec!["8.33", "a"], + ]; + assert_eq!(got, expected); +} + #[test] fn sort_reverse() { let wrk = Workdir::new("sort_reverse");