From 45e9a2bbabce54b9a4aae5a4ff7fb6a9f2a24119 Mon Sep 17 00:00:00 2001 From: Matthew RONCHETTO Date: Fri, 18 Apr 2025 21:06:34 -0700 Subject: [PATCH 1/3] chore: allow sort filter to be case insensitive Closes #587, #831 --- src/builtins/filters/array.rs | 20 +++++++++++++++++++- src/filter_utils.rs | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/builtins/filters/array.rs b/src/builtins/filters/array.rs index f050a6a1..dd11412c 100644 --- a/src/builtins/filters/array.rs +++ b/src/builtins/filters/array.rs @@ -85,7 +85,12 @@ pub fn sort(value: &Value, args: &HashMap) -> Result { Error::msg(format!("attribute '{}' does not reference a field", attribute)) })?; - let mut strategy = get_sort_strategy_for_type(first)?; + let case_sensitive = match args.get("case_sensitive") { + Some(val) => try_get_value!("sort", "case_sensitive", bool, val), + None => true, + }; + + let mut strategy = get_sort_strategy_for_type(first, case_sensitive)?; for v in &arr { let key = dotted_pointer(v, &attribute).ok_or_else(|| { Error::msg(format!("attribute '{}' does not reference a field", attribute)) @@ -451,6 +456,19 @@ mod tests { ); } + #[test] + fn test_sort_case_insensitive() { + let v = to_value(vec!["apple", "coconut", "Banana", "Dog"]).unwrap(); + let mut args = HashMap::new(); + args.insert("case_sensitive".to_string(), to_value(false).unwrap()); + let result = sort(&v, &args); + assert!(result.is_ok()); + assert_eq!( + result.unwrap(), + to_value(vec!["apple", "Banana", "coconut", "Dog"]).unwrap() + ); + } + #[test] fn test_sort_invalid_attribute() { let v = to_value(vec![Foo { a: 3, b: 5 }]).unwrap(); diff --git a/src/filter_utils.rs b/src/filter_utils.rs index 9079ccd8..b4395544 100644 --- a/src/filter_utils.rs +++ b/src/filter_utils.rs @@ -119,7 +119,6 @@ pub struct SortPairs { type SortNumbers = SortPairs; type SortBools = SortPairs; -type SortStrings = SortPairs; type SortArrays = SortPairs; impl SortPairs { @@ -150,13 +149,42 @@ impl SortStrategy for SortPairs { } } -pub fn get_sort_strategy_for_type(ty: &Value) -> Result> { +pub struct SortStrings { + pairs: Vec<(Value, String)>, + case_sensitive: bool, +} +impl SortStrings { + fn new(case_sensitive: bool) -> SortStrings { + SortStrings { pairs: Vec::new(), case_sensitive } + } +} +impl SortStrategy for SortStrings { + fn try_add_pair(&mut self, val: &Value, key: &Value) -> Result<()> { + let key_str = String::get_value(key)?; + self.pairs.push((val.clone(), key_str)); + Ok(()) + } + fn sort(&mut self) -> Vec { + if self.case_sensitive { + self.pairs.sort_by_key(|a| a.1.clone()); + } else { + self.pairs.sort_by_key(|a| a.1.to_lowercase()); + } + + self.pairs.iter().map(|a| a.0.clone()).collect() + } +} + +pub fn get_sort_strategy_for_type( + ty: &Value, + case_sensitive: bool, +) -> Result> { use crate::Value::*; match *ty { Null => Err(Error::msg("Null is not a sortable value")), Bool(_) => Ok(Box::::default()), Number(_) => Ok(Box::::default()), - String(_) => Ok(Box::::default()), + String(_) => Ok(Box::new(SortStrings::new(case_sensitive))), Array(_) => Ok(Box::::default()), Object(_) => Err(Error::msg("Object is not a sortable value")), } From d4ef82b744e600cf23bbb0c9aef7529bef533ad2 Mon Sep 17 00:00:00 2001 From: Matthew RONCHETTO Date: Fri, 18 Apr 2025 21:06:47 -0700 Subject: [PATCH 2/3] docs: add information about case_sensitive argument for sort filter --- docs/content/docs/_index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/content/docs/_index.md b/docs/content/docs/_index.md index 28147f4e..a1faa4f6 100644 --- a/docs/content/docs/_index.md +++ b/docs/content/docs/_index.md @@ -956,6 +956,12 @@ or by age: {{ people | sort(attribute="age") }} ``` +The `case_sensitive` argument (default is true) can be used to control the order of strings. + +```jinja2 +{{ people | sort(attribute="name.1", case_sensitive="false") }} +``` + #### unique Removes duplicate items from an array. The `attribute` argument can be used to select items based on the values of an inner attribute. For strings, the `case_sensitive` argument (default is false) can be used to control the comparison. From af130d114676ac84a12e49b916f2a7594f13cc04 Mon Sep 17 00:00:00 2001 From: Matthew RONCHETTO Date: Fri, 18 Apr 2025 21:27:27 -0700 Subject: [PATCH 3/3] fix(docs): bad usage of sort's case_sensitive argument --- docs/content/docs/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/docs/_index.md b/docs/content/docs/_index.md index a1faa4f6..d566a6f6 100644 --- a/docs/content/docs/_index.md +++ b/docs/content/docs/_index.md @@ -959,7 +959,7 @@ or by age: The `case_sensitive` argument (default is true) can be used to control the order of strings. ```jinja2 -{{ people | sort(attribute="name.1", case_sensitive="false") }} +{{ people | sort(attribute="name.1", case_sensitive=false) }} ``` #### unique