Skip to content

Commit e54f576

Browse files
committed
cmd/sort: add random sort order
1 parent c0d2666 commit e54f576

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

src/cmd/sort.rs

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use std::cmp;
22

33
use CliResult;
4+
use byteorder::{ByteOrder, LittleEndian};
45
use config::{Config, Delimiter};
56
use select::SelectColumns;
67
use util;
8+
use rand::{Rng, SeedableRng, StdRng};
79
use std::str::from_utf8;
810

911
use self::Number::{Float, Int};
@@ -21,6 +23,8 @@ sort options:
2123
See 'xsv select --help' for the format details.
2224
-N, --numeric Compare according to string numerical value
2325
-R, --reverse Reverse order
26+
--random Random order
27+
--seed <number> RNG seed
2428
2529
Common options:
2630
-h, --help Display this message
@@ -39,6 +43,8 @@ struct Args {
3943
flag_select: SelectColumns,
4044
flag_numeric: bool,
4145
flag_reverse: bool,
46+
flag_random: bool,
47+
flag_seed: Option<usize>,
4248
flag_output: Option<String>,
4349
flag_no_headers: bool,
4450
flag_delimiter: Option<Delimiter>,
@@ -48,6 +54,7 @@ pub fn run(argv: &[&str]) -> CliResult<()> {
4854
let args: Args = util::get_args(USAGE, argv)?;
4955
let numeric = args.flag_numeric;
5056
let reverse = args.flag_reverse;
57+
let random = args.flag_random;
5158
let rconfig = Config::new(&args.arg_input)
5259
.delimiter(args.flag_delimiter)
5360
.no_headers(args.flag_no_headers)
@@ -58,27 +65,42 @@ pub fn run(argv: &[&str]) -> CliResult<()> {
5865
let headers = rdr.byte_headers()?.clone();
5966
let sel = rconfig.selection(&headers)?;
6067

68+
// Seeding rng
69+
let seed = args.flag_seed;
70+
let mut rng: StdRng = match seed {
71+
None => {
72+
StdRng::from_rng(rand::thread_rng()).unwrap()
73+
}
74+
Some(seed) => {
75+
let mut buf = [0u8; 32];
76+
LittleEndian::write_u64(&mut buf, seed as u64);
77+
SeedableRng::from_seed(buf)
78+
}
79+
};
80+
6181
let mut all = rdr.byte_records().collect::<Result<Vec<_>, _>>()?;
62-
match (numeric, reverse) {
63-
(false, false) =>
82+
match (numeric, reverse, random) {
83+
(_, _, true) =>
84+
rng.shuffle(&mut all),
85+
(false, false, false) =>
6486
all.sort_by(|r1, r2| {
6587
let a = sel.select(r1);
6688
let b = sel.select(r2);
6789
iter_cmp(a, b)
6890
}),
69-
(true, false) =>
91+
(true, false, false) =>
7092
all.sort_by(|r1, r2| {
7193
let a = sel.select(r1);
7294
let b = sel.select(r2);
7395
iter_cmp_num(a, b)
7496
}),
75-
(false, true) =>
97+
(false, true, false) =>
7698
all.sort_by(|r1, r2| {
7799
let a = sel.select(r1);
78100
let b = sel.select(r2);
79101
iter_cmp(b, a)
80102
}),
81-
(true, true) =>
103+
(true, true, false) =>
82104
all.sort_by(|r1, r2| {
83105
let a = sel.select(r1);
84106
let b = sel.select(r2);

tests/test_sort.rs

+21
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,27 @@ fn sort_reverse() {
128128
assert_eq!(got, expected);
129129
}
130130

131+
#[test]
132+
fn sort_random() {
133+
let wrk = Workdir::new("sort_random");
134+
wrk.create("in.csv", vec![
135+
svec!["R", "S"],
136+
svec!["1", "b"],
137+
svec!["2", "a"],
138+
]);
139+
140+
let mut cmd = wrk.command("sort");
141+
cmd.arg("--random").args(&["--seed", "3"]).arg("in.csv");
142+
143+
let got: Vec<Vec<String>> = wrk.read_stdout(&mut cmd);
144+
let expected = vec![
145+
svec!["R", "S"],
146+
svec!["2", "a"],
147+
svec!["1", "b"],
148+
];
149+
assert_eq!(got, expected);
150+
}
151+
131152
/// Order `a` and `b` lexicographically using `Ord`
132153
pub fn iter_cmp<A, L, R>(mut a: L, mut b: R) -> cmp::Ordering
133154
where A: Ord, L: Iterator<Item=A>, R: Iterator<Item=A> {

0 commit comments

Comments
 (0)