Make comparisons/sort-by more 'global' (#4500)

* Make comparisons/sort-by more 'global'

* Let custom values do their own comparisons
This commit is contained in:
JT 2022-02-16 14:30:37 -05:00 committed by GitHub
parent b64ac9eb7b
commit d620f76a21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 325 additions and 45 deletions

View File

@ -39,11 +39,11 @@ impl Command for DropDuplicates {
NuDataFrame::try_from_columns(vec![ NuDataFrame::try_from_columns(vec![
Column::new( Column::new(
"a".to_string(), "a".to_string(),
vec![Value::test_int(1), Value::test_int(3)], vec![Value::test_int(3), Value::test_int(1)],
), ),
Column::new( Column::new(
"b".to_string(), "b".to_string(),
vec![Value::test_int(2), Value::test_int(4)], vec![Value::test_int(4), Value::test_int(2)],
), ),
]) ])
.expect("simple df for test should not fail") .expect("simple df for test should not fail")

View File

@ -82,6 +82,7 @@ pub fn add_eager_decls(working_set: &mut StateWorkingSet) {
DataTypes, DataTypes,
DescribeDF, DescribeDF,
DropDF, DropDF,
DropDuplicates,
DropNulls, DropNulls,
Dummies, Dummies,
FilterWith, FilterWith,

View File

@ -146,3 +146,16 @@ fn ls_sort_by_type_name_insensitive() {
let json_output = r#"[{"name": "C","type": "Dir"},{"name": "a.txt","type": "File"},{"name": "B.txt","type": "File"}]"#; let json_output = r#"[{"name": "C","type": "Dir"},{"name": "a.txt","type": "File"},{"name": "B.txt","type": "File"}]"#;
assert_eq!(actual.out, json_output); assert_eq!(actual.out, json_output);
} }
#[test]
fn sort_different_types() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
[a, 1, b, 2, c, 3, [4, 5, 6], d, 4, [1, 2, 3]] | sort-by | to json --raw
"#
));
let json_output = r#"[1,2,3,4,"a","b","c","d",[1,2,3],[4,5,6]]"#;
assert_eq!(actual.out, json_output);
}

View File

@ -2,7 +2,7 @@ use super::Expression;
use crate::Span; use crate::Span;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, PartialOrd, Serialize, Deserialize)]
pub enum PathMember { pub enum PathMember {
String { val: String, span: Span }, String { val: String, span: Span },
Int { val: usize, span: Span }, Int { val: usize, span: Span },
@ -18,7 +18,7 @@ impl PartialEq for PathMember {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct CellPath { pub struct CellPath {
pub members: Vec<PathMember>, pub members: Vec<PathMember>,
} }

View File

@ -50,7 +50,7 @@ impl Display for Operator {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum RangeInclusion { pub enum RangeInclusion {
Inclusive, Inclusive,
RightExclusive, RightExclusive,

View File

@ -30,6 +30,8 @@ pub use custom_value::CustomValue;
use crate::ShellError; use crate::ShellError;
/// Core structured values that pass through the pipeline in engine-q /// Core structured values that pass through the pipeline in engine-q
// NOTE: Please do not reorder these enum cases without thinking through the
// impact on the PartialOrd implementation and the global sort order
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum Value { pub enum Value {
Bool { Bool {
@ -40,6 +42,10 @@ pub enum Value {
val: i64, val: i64,
span: Span, span: Span,
}, },
Float {
val: f64,
span: Span,
},
Filesize { Filesize {
val: i64, val: i64,
span: Span, span: Span,
@ -56,10 +62,6 @@ pub enum Value {
val: Box<Range>, val: Box<Range>,
span: Span, span: Span,
}, },
Float {
val: f64,
span: Span,
},
String { String {
val: String, val: String,
span: Span, span: Span,
@ -901,49 +903,295 @@ impl PartialOrd for Value {
} }
match (self, other) { match (self, other) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => lhs.partial_cmp(rhs), (Value::Bool { val: lhs, .. }, rhs) => match rhs {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => lhs.partial_cmp(rhs), Value::Bool { val: rhs, .. } => lhs.partial_cmp(rhs),
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => { Value::Int { .. } => Some(Ordering::Less),
compare_floats(*lhs, *rhs) Value::Float { .. } => Some(Ordering::Less),
} Value::Filesize { .. } => Some(Ordering::Less),
(Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => lhs.partial_cmp(rhs), Value::Duration { .. } => Some(Ordering::Less),
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => { Value::Date { .. } => Some(Ordering::Less),
lhs.partial_cmp(rhs) Value::Range { .. } => Some(Ordering::Less),
} Value::String { .. } => Some(Ordering::Less),
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { Value::Record { .. } => Some(Ordering::Less),
compare_floats(*lhs as f64, *rhs) Value::List { .. } => Some(Ordering::Less),
} Value::Block { .. } => Some(Ordering::Less),
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => { Value::Nothing { .. } => Some(Ordering::Less),
compare_floats(*lhs, *rhs as f64) Value::Error { .. } => Some(Ordering::Less),
} Value::Binary { .. } => Some(Ordering::Less),
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => { Value::CellPath { .. } => Some(Ordering::Less),
lhs.partial_cmp(rhs) Value::CustomValue { .. } => Some(Ordering::Less),
} },
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => { (Value::Int { val: lhs, .. }, rhs) => match rhs {
lhs.partial_cmp(rhs) Value::Bool { .. } => Some(Ordering::Greater),
} Value::Int { val: rhs, .. } => lhs.partial_cmp(rhs),
(Value::Block { val: b1, .. }, Value::Block { val: b2, .. }) if b1 == b2 => { Value::Float { val: rhs, .. } => compare_floats(*lhs as f64, *rhs),
Some(Ordering::Equal) Value::Filesize { .. } => Some(Ordering::Less),
} Value::Duration { .. } => Some(Ordering::Less),
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => lhs.partial_cmp(rhs), Value::Date { .. } => Some(Ordering::Less),
Value::Range { .. } => Some(Ordering::Less),
Value::String { .. } => Some(Ordering::Less),
Value::Record { .. } => Some(Ordering::Less),
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Float { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { val: rhs, .. } => compare_floats(*lhs, *rhs as f64),
Value::Float { val: rhs, .. } => compare_floats(*lhs, *rhs),
Value::Filesize { .. } => Some(Ordering::Less),
Value::Duration { .. } => Some(Ordering::Less),
Value::Date { .. } => Some(Ordering::Less),
Value::Range { .. } => Some(Ordering::Less),
Value::String { .. } => Some(Ordering::Less),
Value::Record { .. } => Some(Ordering::Less),
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Filesize { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::Duration { .. } => Some(Ordering::Less),
Value::Date { .. } => Some(Ordering::Less),
Value::Range { .. } => Some(Ordering::Less),
Value::String { .. } => Some(Ordering::Less),
Value::Record { .. } => Some(Ordering::Less),
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Duration { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::Date { .. } => Some(Ordering::Less),
Value::Range { .. } => Some(Ordering::Less),
Value::String { .. } => Some(Ordering::Less),
Value::Record { .. } => Some(Ordering::Less),
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Date { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::Range { .. } => Some(Ordering::Less),
Value::String { .. } => Some(Ordering::Less),
Value::Record { .. } => Some(Ordering::Less),
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Range { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::String { .. } => Some(Ordering::Less),
Value::Record { .. } => Some(Ordering::Less),
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::String { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::Record { .. } => Some(Ordering::Less),
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
( (
Value::Record { Value::Record {
vals: lhs, cols: lhs_cols,
cols: lhs_headers, vals: lhs_vals,
.. ..
}, },
rhs,
) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { Value::Record {
vals: rhs, cols: rhs_cols,
cols: rhs_headers, vals: rhs_vals,
.. ..
}, } => {
) if lhs_headers == rhs_headers && lhs == rhs => Some(Ordering::Equal), let result = lhs_cols.partial_cmp(rhs_cols);
(Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => { if result == Some(Ordering::Equal) {
lhs.partial_cmp(rhs) lhs_vals.partial_cmp(rhs_vals)
} } else {
result
}
}
Value::List { .. } => Some(Ordering::Less),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::List { vals: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { .. } => Some(Ordering::Greater),
Value::List { vals: rhs, .. } => lhs.partial_cmp(rhs),
Value::Block { .. } => Some(Ordering::Less),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Block { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { .. } => Some(Ordering::Greater),
Value::List { .. } => Some(Ordering::Greater),
Value::Block { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::Nothing { .. } => Some(Ordering::Less),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Nothing { .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { .. } => Some(Ordering::Greater),
Value::List { .. } => Some(Ordering::Greater),
Value::Block { .. } => Some(Ordering::Greater),
Value::Nothing { .. } => Some(Ordering::Equal),
Value::Error { .. } => Some(Ordering::Less),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Error { .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { .. } => Some(Ordering::Greater),
Value::List { .. } => Some(Ordering::Greater),
Value::Block { .. } => Some(Ordering::Greater),
Value::Nothing { .. } => Some(Ordering::Greater),
Value::Error { .. } => Some(Ordering::Equal),
Value::Binary { .. } => Some(Ordering::Less),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::Binary { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { .. } => Some(Ordering::Greater),
Value::List { .. } => Some(Ordering::Greater),
Value::Block { .. } => Some(Ordering::Greater),
Value::Nothing { .. } => Some(Ordering::Greater),
Value::Error { .. } => Some(Ordering::Greater),
Value::Binary { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::CellPath { .. } => Some(Ordering::Less),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::CellPath { val: lhs, .. }, rhs) => match rhs {
Value::Bool { .. } => Some(Ordering::Greater),
Value::Int { .. } => Some(Ordering::Greater),
Value::Float { .. } => Some(Ordering::Greater),
Value::Filesize { .. } => Some(Ordering::Greater),
Value::Duration { .. } => Some(Ordering::Greater),
Value::Date { .. } => Some(Ordering::Greater),
Value::Range { .. } => Some(Ordering::Greater),
Value::String { .. } => Some(Ordering::Greater),
Value::Record { .. } => Some(Ordering::Greater),
Value::List { .. } => Some(Ordering::Greater),
Value::Block { .. } => Some(Ordering::Greater),
Value::Nothing { .. } => Some(Ordering::Greater),
Value::Error { .. } => Some(Ordering::Greater),
Value::Binary { .. } => Some(Ordering::Greater),
Value::CellPath { val: rhs, .. } => lhs.partial_cmp(rhs),
Value::CustomValue { .. } => Some(Ordering::Less),
},
(Value::CustomValue { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs), (Value::CustomValue { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
(Value::Nothing { .. }, Value::Nothing { .. }) => Some(Ordering::Equal),
(_, _) => None,
} }
} }
} }

View File

@ -129,6 +129,24 @@ impl Range {
} }
} }
impl PartialOrd for Range {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.from.partial_cmp(&other.from) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.incr.partial_cmp(&other.incr) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.to.partial_cmp(&other.to) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
self.inclusion.partial_cmp(&other.inclusion)
}
}
pub struct RangeIterator { pub struct RangeIterator {
curr: Value, curr: Value,
end: Value, end: Value,