Skip to content

Commit

Permalink
cmd/sort: add random sort order
Browse files Browse the repository at this point in the history
  • Loading branch information
pjsier committed Aug 21, 2019
1 parent c0d2666 commit e54f576
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
32 changes: 27 additions & 5 deletions src/cmd/sort.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::cmp;

use CliResult;
use byteorder::{ByteOrder, LittleEndian};
use config::{Config, Delimiter};
use select::SelectColumns;
use util;
use rand::{Rng, SeedableRng, StdRng};
use std::str::from_utf8;

use self::Number::{Float, Int};
Expand All @@ -21,6 +23,8 @@ sort options:
See 'xsv select --help' for the format details.
-N, --numeric Compare according to string numerical value
-R, --reverse Reverse order
--random Random order
--seed <number> RNG seed
Common options:
-h, --help Display this message
Expand All @@ -39,6 +43,8 @@ struct Args {
flag_select: SelectColumns,
flag_numeric: bool,
flag_reverse: bool,
flag_random: bool,
flag_seed: Option<usize>,
flag_output: Option<String>,
flag_no_headers: bool,
flag_delimiter: Option<Delimiter>,
Expand All @@ -48,6 +54,7 @@ pub fn run(argv: &[&str]) -> CliResult<()> {
let args: Args = util::get_args(USAGE, argv)?;
let numeric = args.flag_numeric;
let reverse = args.flag_reverse;
let random = args.flag_random;
let rconfig = Config::new(&args.arg_input)
.delimiter(args.flag_delimiter)
.no_headers(args.flag_no_headers)
Expand All @@ -58,27 +65,42 @@ pub fn run(argv: &[&str]) -> CliResult<()> {
let headers = rdr.byte_headers()?.clone();
let sel = rconfig.selection(&headers)?;

// Seeding rng
let seed = args.flag_seed;
let mut rng: StdRng = match seed {
None => {
StdRng::from_rng(rand::thread_rng()).unwrap()
}
Some(seed) => {
let mut buf = [0u8; 32];
LittleEndian::write_u64(&mut buf, seed as u64);
SeedableRng::from_seed(buf)
}
};

let mut all = rdr.byte_records().collect::<Result<Vec<_>, _>>()?;
match (numeric, reverse) {
(false, false) =>
match (numeric, reverse, random) {
(_, _, true) =>
rng.shuffle(&mut all),
(false, false, false) =>
all.sort_by(|r1, r2| {
let a = sel.select(r1);
let b = sel.select(r2);
iter_cmp(a, b)
}),
(true, false) =>
(true, false, false) =>
all.sort_by(|r1, r2| {
let a = sel.select(r1);
let b = sel.select(r2);
iter_cmp_num(a, b)
}),
(false, true) =>
(false, true, false) =>
all.sort_by(|r1, r2| {
let a = sel.select(r1);
let b = sel.select(r2);
iter_cmp(b, a)
}),
(true, true) =>
(true, true, false) =>
all.sort_by(|r1, r2| {
let a = sel.select(r1);
let b = sel.select(r2);
Expand Down
21 changes: 21 additions & 0 deletions tests/test_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,27 @@ fn sort_reverse() {
assert_eq!(got, expected);
}

#[test]
fn sort_random() {
let wrk = Workdir::new("sort_random");
wrk.create("in.csv", vec![
svec!["R", "S"],
svec!["1", "b"],
svec!["2", "a"],
]);

let mut cmd = wrk.command("sort");
cmd.arg("--random").args(&["--seed", "3"]).arg("in.csv");

let got: Vec<Vec<String>> = wrk.read_stdout(&mut cmd);
let expected = vec![
svec!["R", "S"],
svec!["2", "a"],
svec!["1", "b"],
];
assert_eq!(got, expected);
}

/// Order `a` and `b` lexicographically using `Ord`
pub fn iter_cmp<A, L, R>(mut a: L, mut b: R) -> cmp::Ordering
where A: Ord, L: Iterator<Item=A>, R: Iterator<Item=A> {
Expand Down

0 comments on commit e54f576

Please sign in to comment.