Skip to main content

shannon_nu_command/
sort_utils.rs

1use nu_engine::ClosureEval;
2use nu_protocol::shell_error::generic::GenericError;
3use nu_protocol::{PipelineData, Record, ShellError, Span, Value, ast::CellPath};
4use nu_utils::IgnoreCaseExt;
5use std::cmp::Ordering;
6
7/// A specification of sort order for `sort_by`.
8///
9/// A closure comparator allows the user to return custom ordering to sort by.
10/// A cell path comparator uses the value referred to by the cell path as the sorting key.
11pub enum Comparator {
12    KeyClosure(ClosureEval),
13    CustomClosure(ClosureEval),
14    CellPath(CellPath),
15}
16
17/// Sort a slice of `Value`s.
18///
19/// Sort has the following invariants, in order of precedence:
20/// - Null values (Nothing type) are always sorted to the end.
21/// - For natural sort, numeric values (numeric strings, ints, and floats) appear first, sorted by numeric value
22/// - Values appear by order of `Value`'s `PartialOrd`.
23/// - Sorting for values with equal ordering is stable.
24///
25/// Generally, values of different types are ordered by order of appearance in the `Value` enum.
26/// However, this is not always the case. For example, ints and floats will be grouped together since
27/// `Value`'s `PartialOrd` defines a non-decreasing ordering between non-decreasing integers and floats.
28pub fn sort(vec: &mut [Value], insensitive: bool, natural: bool) -> Result<(), ShellError> {
29    // allow the comparator function to indicate error
30    // by mutating this option captured by the closure,
31    // since sort_by closure must be infallible
32    let mut compare_err: Option<ShellError> = None;
33
34    vec.sort_by(|a, b| {
35        // we've already hit an error, bail out now
36        if compare_err.is_some() {
37            return Ordering::Equal;
38        }
39
40        compare_values(a, b, insensitive, natural).unwrap_or_else(|err| {
41            compare_err.get_or_insert(err);
42            Ordering::Equal
43        })
44    });
45
46    if let Some(err) = compare_err {
47        Err(err)
48    } else {
49        Ok(())
50    }
51}
52
53/// Sort a slice of `Value`s by criteria specified by one or multiple `Comparator`s.
54pub fn sort_by(
55    vec: &mut [Value],
56    mut comparators: Vec<Comparator>,
57    head_span: Span,
58    insensitive: bool,
59    natural: bool,
60) -> Result<(), ShellError> {
61    if comparators.is_empty() {
62        return Err(ShellError::Generic(GenericError::new(
63            "expected name",
64            "requires a cell path or closure to sort data",
65            head_span,
66        )));
67    }
68
69    // allow the comparator function to indicate error
70    // by mutating this option captured by the closure,
71    // since sort_by closure must be infallible
72    let mut compare_err: Option<ShellError> = None;
73
74    vec.sort_by(|a, b| {
75        compare_by(
76            a,
77            b,
78            &mut comparators,
79            head_span,
80            insensitive,
81            natural,
82            &mut compare_err,
83        )
84    });
85
86    if let Some(err) = compare_err {
87        Err(err)
88    } else {
89        Ok(())
90    }
91}
92
93/// Sort a record's key-value pairs.
94///
95/// Can sort by key or by value.
96pub fn sort_record(
97    record: Record,
98    sort_by_value: bool,
99    reverse: bool,
100    insensitive: bool,
101    natural: bool,
102) -> Result<Record, ShellError> {
103    let mut input_pairs: Vec<(String, Value)> = record.into_iter().collect();
104
105    // allow the comparator function to indicate error
106    // by mutating this option captured by the closure,
107    // since sort_by closure must be infallible
108    let mut compare_err: Option<ShellError> = None;
109
110    if sort_by_value {
111        input_pairs.sort_by(|a, b| {
112            // we've already hit an error, bail out now
113            if compare_err.is_some() {
114                return Ordering::Equal;
115            }
116
117            compare_values(&a.1, &b.1, insensitive, natural).unwrap_or_else(|err| {
118                compare_err.get_or_insert(err);
119                Ordering::Equal
120            })
121        });
122    } else {
123        input_pairs.sort_by(|a, b| compare_strings(&a.0, &b.0, insensitive, natural));
124    };
125
126    if let Some(err) = compare_err {
127        return Err(err);
128    }
129
130    if reverse {
131        input_pairs.reverse()
132    }
133
134    Ok(input_pairs.into_iter().collect())
135}
136
137pub fn compare_by(
138    left: &Value,
139    right: &Value,
140    comparators: &mut [Comparator],
141    span: Span,
142    insensitive: bool,
143    natural: bool,
144    error: &mut Option<ShellError>,
145) -> Ordering {
146    // we've already hit an error, bail out now
147    if error.is_some() {
148        return Ordering::Equal;
149    }
150    for cmp in comparators.iter_mut() {
151        let result = match cmp {
152            Comparator::CellPath(cell_path) => {
153                compare_cell_path(left, right, cell_path, insensitive, natural)
154            }
155            Comparator::KeyClosure(closure) => {
156                compare_key_closure(left, right, closure, span, insensitive, natural)
157            }
158            Comparator::CustomClosure(closure) => {
159                compare_custom_closure(left, right, closure, span)
160            }
161        };
162        match result {
163            Ok(Ordering::Equal) => {}
164            Ok(ordering) => return ordering,
165            Err(err) => {
166                // don't bother continuing through the remaining comparators as we've hit an error
167                // don't overwrite if there's an existing error
168                error.get_or_insert(err);
169                return Ordering::Equal;
170            }
171        }
172    }
173    Ordering::Equal
174}
175
176/// Determines whether a value should be sorted as a string
177///
178/// If we're natural sorting, we want to sort strings, integers, and floats alphanumerically, so we should string sort.
179/// Otherwise, we only want to string sort if both values are strings or globs (to enable case insensitive comparison)
180fn should_sort_as_string(val: &Value, natural: bool) -> bool {
181    matches!(
182        (val, natural),
183        (&Value::String { .. }, _)
184            | (&Value::Glob { .. }, _)
185            | (&Value::Int { .. }, true)
186            | (&Value::Float { .. }, true)
187    )
188}
189
190/// Simple wrapper around `should_sort_as_string` to determine if two values
191/// should be compared as strings.
192fn should_string_compare(left: &Value, right: &Value, natural: bool) -> bool {
193    should_sort_as_string(left, natural) && should_sort_as_string(right, natural)
194}
195
196pub fn compare_values(
197    left: &Value,
198    right: &Value,
199    insensitive: bool,
200    natural: bool,
201) -> Result<Ordering, ShellError> {
202    if should_string_compare(left, right, natural) {
203        Ok(compare_strings(
204            &left.coerce_str()?,
205            &right.coerce_str()?,
206            insensitive,
207            natural,
208        ))
209    } else {
210        Ok(left.partial_cmp(right).unwrap_or(Ordering::Equal))
211    }
212}
213
214pub fn compare_strings(left: &str, right: &str, insensitive: bool, natural: bool) -> Ordering {
215    fn compare_inner<T>(left: T, right: T, natural: bool) -> Ordering
216    where
217        T: AsRef<str> + Ord,
218    {
219        if natural {
220            alphanumeric_sort::compare_str(left, right)
221        } else {
222            left.cmp(&right)
223        }
224    }
225
226    // only allocate a String if necessary for case folding
227    if insensitive {
228        compare_inner(left.to_folded_case(), right.to_folded_case(), natural)
229    } else {
230        compare_inner(left, right, natural)
231    }
232}
233
234pub fn compare_cell_path(
235    left: &Value,
236    right: &Value,
237    cell_path: &CellPath,
238    insensitive: bool,
239    natural: bool,
240) -> Result<Ordering, ShellError> {
241    let left = left.follow_cell_path(&cell_path.members)?;
242    let right = right.follow_cell_path(&cell_path.members)?;
243    compare_values(&left, &right, insensitive, natural)
244}
245
246pub fn compare_key_closure(
247    left: &Value,
248    right: &Value,
249    closure_eval: &mut ClosureEval,
250    span: Span,
251    insensitive: bool,
252    natural: bool,
253) -> Result<Ordering, ShellError> {
254    let left_key = closure_eval
255        .run_with_value(left.clone())?
256        .into_value(span)?;
257    let right_key = closure_eval
258        .run_with_value(right.clone())?
259        .into_value(span)?;
260    compare_values(&left_key, &right_key, insensitive, natural)
261}
262
263pub fn compare_custom_closure(
264    left: &Value,
265    right: &Value,
266    closure_eval: &mut ClosureEval,
267    span: Span,
268) -> Result<Ordering, ShellError> {
269    closure_eval
270        .add_arg(left.clone())
271        .add_arg(right.clone())
272        .run_with_input(PipelineData::value(
273            Value::list(vec![left.clone(), right.clone()], span),
274            None,
275        ))
276        .and_then(|data| data.into_value(span))
277        .map(|val| {
278            if val.is_true() {
279                Ordering::Less
280            } else {
281                Ordering::Greater
282            }
283        })
284}
285
286#[cfg(test)]
287mod tests {
288    use super::*;
289    use nu_protocol::{ast::PathMember, casing::Casing, record};
290
291    #[test]
292    fn test_sort_basic() {
293        let mut list = vec![
294            Value::test_string("foo"),
295            Value::test_int(2),
296            Value::test_int(3),
297            Value::test_string("bar"),
298            Value::test_int(1),
299            Value::test_string("baz"),
300        ];
301
302        assert!(sort(&mut list, false, false).is_ok());
303        assert_eq!(
304            list,
305            vec![
306                Value::test_int(1),
307                Value::test_int(2),
308                Value::test_int(3),
309                Value::test_string("bar"),
310                Value::test_string("baz"),
311                Value::test_string("foo")
312            ]
313        );
314    }
315
316    #[test]
317    fn test_sort_nothing() {
318        // Nothing values should always be sorted to the end of any list
319        let mut list = vec![
320            Value::test_int(1),
321            Value::test_nothing(),
322            Value::test_int(2),
323            Value::test_string("foo"),
324            Value::test_nothing(),
325            Value::test_string("bar"),
326        ];
327
328        assert!(sort(&mut list, false, false).is_ok());
329        assert_eq!(
330            list,
331            vec![
332                Value::test_int(1),
333                Value::test_int(2),
334                Value::test_string("bar"),
335                Value::test_string("foo"),
336                Value::test_nothing(),
337                Value::test_nothing()
338            ]
339        );
340
341        // Ensure that nothing values are sorted after *all* types,
342        // even types which may follow `Nothing` in the PartialOrd order
343
344        // unstable_name_collision
345        // can be switched to std intersperse when stabilized
346        let mut values: Vec<Value> =
347            itertools::intersperse(Value::test_values(), Value::test_nothing()).collect();
348
349        let nulls = values
350            .iter()
351            .filter(|item| item == &&Value::test_nothing())
352            .count();
353
354        assert!(sort(&mut values, false, false).is_ok());
355
356        // check if the last `nulls` values of the sorted list are indeed null
357        assert_eq!(&values[(nulls - 1)..], vec![Value::test_nothing(); nulls])
358    }
359
360    #[test]
361    fn test_sort_natural_basic() {
362        let mut list = vec![
363            Value::test_string("foo99"),
364            Value::test_string("foo9"),
365            Value::test_string("foo1"),
366            Value::test_string("foo100"),
367            Value::test_string("foo10"),
368            Value::test_string("1"),
369            Value::test_string("10"),
370            Value::test_string("100"),
371            Value::test_string("9"),
372            Value::test_string("99"),
373        ];
374
375        assert!(sort(&mut list, false, false).is_ok());
376        assert_eq!(
377            list,
378            vec![
379                Value::test_string("1"),
380                Value::test_string("10"),
381                Value::test_string("100"),
382                Value::test_string("9"),
383                Value::test_string("99"),
384                Value::test_string("foo1"),
385                Value::test_string("foo10"),
386                Value::test_string("foo100"),
387                Value::test_string("foo9"),
388                Value::test_string("foo99"),
389            ]
390        );
391
392        assert!(sort(&mut list, false, true).is_ok());
393        assert_eq!(
394            list,
395            vec![
396                Value::test_string("1"),
397                Value::test_string("9"),
398                Value::test_string("10"),
399                Value::test_string("99"),
400                Value::test_string("100"),
401                Value::test_string("foo1"),
402                Value::test_string("foo9"),
403                Value::test_string("foo10"),
404                Value::test_string("foo99"),
405                Value::test_string("foo100"),
406            ]
407        );
408    }
409
410    #[test]
411    fn test_sort_natural_mixed_types() {
412        let mut list = vec![
413            Value::test_string("1"),
414            Value::test_int(99),
415            Value::test_int(1),
416            Value::test_float(1000.0),
417            Value::test_int(9),
418            Value::test_string("9"),
419            Value::test_int(100),
420            Value::test_string("99"),
421            Value::test_float(2.0),
422            Value::test_string("100"),
423            Value::test_int(10),
424            Value::test_string("10"),
425        ];
426
427        assert!(sort(&mut list, false, false).is_ok());
428        assert_eq!(
429            list,
430            vec![
431                Value::test_int(1),
432                Value::test_float(2.0),
433                Value::test_int(9),
434                Value::test_int(10),
435                Value::test_int(99),
436                Value::test_int(100),
437                Value::test_float(1000.0),
438                Value::test_string("1"),
439                Value::test_string("10"),
440                Value::test_string("100"),
441                Value::test_string("9"),
442                Value::test_string("99")
443            ]
444        );
445
446        assert!(sort(&mut list, false, true).is_ok());
447        assert_eq!(
448            list,
449            vec![
450                Value::test_int(1),
451                Value::test_string("1"),
452                Value::test_float(2.0),
453                Value::test_int(9),
454                Value::test_string("9"),
455                Value::test_int(10),
456                Value::test_string("10"),
457                Value::test_int(99),
458                Value::test_string("99"),
459                Value::test_int(100),
460                Value::test_string("100"),
461                Value::test_float(1000.0),
462            ]
463        );
464    }
465
466    #[test]
467    fn test_sort_natural_no_numeric_values() {
468        // If list contains no numeric strings, it should be sorted the
469        // same with or without natural sorting
470        let mut normal = vec![
471            Value::test_string("golf"),
472            Value::test_bool(false),
473            Value::test_string("alfa"),
474            Value::test_string("echo"),
475            Value::test_int(7),
476            Value::test_int(10),
477            Value::test_bool(true),
478            Value::test_string("uniform"),
479            Value::test_int(3),
480            Value::test_string("tango"),
481        ];
482        let mut natural = normal.clone();
483
484        assert!(sort(&mut normal, false, false).is_ok());
485        assert!(sort(&mut natural, false, true).is_ok());
486        assert_eq!(normal, natural);
487    }
488
489    #[test]
490    fn test_sort_natural_type_order() {
491        // This test is to prevent regression to a previous natural sort behavior
492        // where values of different types would be intermixed.
493        // Only numeric values (ints, floats, and numeric strings) should be intermixed
494        //
495        // This list would previously be incorrectly sorted like this:
496        // ╭────┬─────────╮
497        // │  0 │       1 │
498        // │  1 │ golf    │
499        // │  2 │ false   │
500        // │  3 │       7 │
501        // │  4 │      10 │
502        // │  5 │ alfa    │
503        // │  6 │ true    │
504        // │  7 │ uniform │
505        // │  8 │ true    │
506        // │  9 │       3 │
507        // │ 10 │ false   │
508        // │ 11 │ tango   │
509        // ╰────┴─────────╯
510
511        let mut list = vec![
512            Value::test_string("golf"),
513            Value::test_int(1),
514            Value::test_bool(false),
515            Value::test_string("alfa"),
516            Value::test_int(7),
517            Value::test_int(10),
518            Value::test_bool(true),
519            Value::test_string("uniform"),
520            Value::test_bool(true),
521            Value::test_int(3),
522            Value::test_bool(false),
523            Value::test_string("tango"),
524        ];
525
526        assert!(sort(&mut list, false, true).is_ok());
527        assert_eq!(
528            list,
529            vec![
530                Value::test_bool(false),
531                Value::test_bool(false),
532                Value::test_bool(true),
533                Value::test_bool(true),
534                Value::test_int(1),
535                Value::test_int(3),
536                Value::test_int(7),
537                Value::test_int(10),
538                Value::test_string("alfa"),
539                Value::test_string("golf"),
540                Value::test_string("tango"),
541                Value::test_string("uniform")
542            ]
543        );
544
545        // Only ints, floats, and numeric strings should be intermixed
546        // While binary primitives and datetimes can be coerced into strings, it doesn't make sense to sort them with numbers
547        // Binary primitives can hold multiple values, not just one, so shouldn't be compared to single values
548        // Datetimes don't have a single obvious numeric representation, and if we chose one it would be ambiguous to the user
549
550        let year_three = chrono::NaiveDate::from_ymd_opt(3, 1, 1)
551            .unwrap()
552            .and_hms_opt(0, 0, 0)
553            .unwrap()
554            .and_utc();
555
556        let mut list = vec![
557            Value::test_int(10),
558            Value::test_float(6.0),
559            Value::test_int(1),
560            Value::test_binary([3]),
561            Value::test_string("2"),
562            Value::test_date(year_three.into()),
563            Value::test_int(4),
564            Value::test_binary([52]),
565            Value::test_float(9.0),
566            Value::test_string("5"),
567            Value::test_date(chrono::DateTime::UNIX_EPOCH.into()),
568            Value::test_int(7),
569            Value::test_string("8"),
570            Value::test_float(3.0),
571            Value::test_string("foobar"),
572        ];
573        assert!(sort(&mut list, false, true).is_ok());
574        assert_eq!(
575            list,
576            vec![
577                Value::test_int(1),
578                Value::test_string("2"),
579                Value::test_float(3.0),
580                Value::test_int(4),
581                Value::test_string("5"),
582                Value::test_float(6.0),
583                Value::test_int(7),
584                Value::test_string("8"),
585                Value::test_float(9.0),
586                Value::test_int(10),
587                Value::test_string("foobar"),
588                // the ordering of date and binary here may change if the PartialOrd order is changed,
589                // but they should not be intermixed with the above
590                Value::test_date(year_three.into()),
591                Value::test_date(chrono::DateTime::UNIX_EPOCH.into()),
592                Value::test_binary([3]),
593                Value::test_binary([52]),
594            ]
595        );
596    }
597
598    #[test]
599    fn test_sort_insensitive() {
600        // Test permutations between insensitive and natural
601        // Ensure that strings with equal insensitive orderings
602        // are sorted stably. (FOO then foo, bar then BAR)
603        let source = vec![
604            Value::test_string("FOO"),
605            Value::test_string("foo"),
606            Value::test_int(100),
607            Value::test_string("9"),
608            Value::test_string("bar"),
609            Value::test_int(10),
610            Value::test_string("baz"),
611            Value::test_string("BAR"),
612        ];
613        let mut list;
614
615        // sensitive + non-natural
616        list = source.clone();
617        assert!(sort(&mut list, false, false).is_ok());
618        assert_eq!(
619            list,
620            vec![
621                Value::test_int(10),
622                Value::test_int(100),
623                Value::test_string("9"),
624                Value::test_string("BAR"),
625                Value::test_string("FOO"),
626                Value::test_string("bar"),
627                Value::test_string("baz"),
628                Value::test_string("foo"),
629            ]
630        );
631
632        // sensitive + natural
633        list = source.clone();
634        assert!(sort(&mut list, false, true).is_ok());
635        assert_eq!(
636            list,
637            vec![
638                Value::test_string("9"),
639                Value::test_int(10),
640                Value::test_int(100),
641                Value::test_string("BAR"),
642                Value::test_string("FOO"),
643                Value::test_string("bar"),
644                Value::test_string("baz"),
645                Value::test_string("foo"),
646            ]
647        );
648
649        // insensitive + non-natural
650        list = source.clone();
651        assert!(sort(&mut list, true, false).is_ok());
652        assert_eq!(
653            list,
654            vec![
655                Value::test_int(10),
656                Value::test_int(100),
657                Value::test_string("9"),
658                Value::test_string("bar"),
659                Value::test_string("BAR"),
660                Value::test_string("baz"),
661                Value::test_string("FOO"),
662                Value::test_string("foo"),
663            ]
664        );
665
666        // insensitive + natural
667        list = source.clone();
668        assert!(sort(&mut list, true, true).is_ok());
669        assert_eq!(
670            list,
671            vec![
672                Value::test_string("9"),
673                Value::test_int(10),
674                Value::test_int(100),
675                Value::test_string("bar"),
676                Value::test_string("BAR"),
677                Value::test_string("baz"),
678                Value::test_string("FOO"),
679                Value::test_string("foo"),
680            ]
681        );
682    }
683
684    // Helper function to assert that two records are equal
685    // with their key-value pairs in the same order
686    fn assert_record_eq(a: Record, b: Record) {
687        assert_eq!(
688            a.into_iter().collect::<Vec<_>>(),
689            b.into_iter().collect::<Vec<_>>(),
690        )
691    }
692
693    #[test]
694    fn test_sort_record_keys() {
695        // Basic record sort test
696        let record = record! {
697            "golf" => Value::test_string("bar"),
698            "alfa" => Value::test_string("foo"),
699            "echo" => Value::test_int(123),
700        };
701
702        let sorted = sort_record(record, false, false, false, false).unwrap();
703        assert_record_eq(
704            sorted,
705            record! {
706                "alfa" => Value::test_string("foo"),
707                "echo" => Value::test_int(123),
708                "golf" => Value::test_string("bar"),
709            },
710        );
711    }
712
713    #[test]
714    fn test_sort_record_values() {
715        // This test is to prevent a regression where integers and strings would be
716        // intermixed non-naturally when sorting a record by value without the natural flag:
717        //
718        // This record would previously be incorrectly sorted like this:
719        // ╭─────────┬─────╮
720        // │ alfa    │ 1   │
721        // │ charlie │ 1   │
722        // │ india   │ 10  │
723        // │ juliett │ 10  │
724        // │ foxtrot │ 100 │
725        // │ hotel   │ 100 │
726        // │ delta   │ 9   │
727        // │ echo    │ 9   │
728        // │ bravo   │ 99  │
729        // │ golf    │ 99  │
730        // ╰─────────┴─────╯
731
732        let record = record! {
733            "alfa" => Value::test_string("1"),
734            "bravo" => Value::test_int(99),
735            "charlie" => Value::test_int(1),
736            "delta" => Value::test_int(9),
737            "echo" => Value::test_string("9"),
738            "foxtrot" => Value::test_int(100),
739            "golf" => Value::test_string("99"),
740            "hotel" => Value::test_string("100"),
741            "india" => Value::test_int(10),
742            "juliett" => Value::test_string("10"),
743        };
744
745        // non-natural sort
746        let sorted = sort_record(record.clone(), true, false, false, false).unwrap();
747        assert_record_eq(
748            sorted,
749            record! {
750                "charlie" => Value::test_int(1),
751                "delta" => Value::test_int(9),
752                "india" => Value::test_int(10),
753                "bravo" => Value::test_int(99),
754                "foxtrot" => Value::test_int(100),
755                "alfa" => Value::test_string("1"),
756                "juliett" => Value::test_string("10"),
757                "hotel" => Value::test_string("100"),
758                "echo" => Value::test_string("9"),
759                "golf" => Value::test_string("99"),
760            },
761        );
762
763        // natural sort
764        let sorted = sort_record(record.clone(), true, false, false, true).unwrap();
765        assert_record_eq(
766            sorted,
767            record! {
768                "alfa" => Value::test_string("1"),
769                "charlie" => Value::test_int(1),
770                "delta" => Value::test_int(9),
771                "echo" => Value::test_string("9"),
772                "india" => Value::test_int(10),
773                "juliett" => Value::test_string("10"),
774                "bravo" => Value::test_int(99),
775                "golf" => Value::test_string("99"),
776                "foxtrot" => Value::test_int(100),
777                "hotel" => Value::test_string("100"),
778            },
779        );
780    }
781
782    #[test]
783    fn test_sort_equivalent() {
784        // Ensure that sort, sort_by, and record sort have equivalent sorting logic
785        let phonetic = vec![
786            "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india",
787            "juliett", "kilo", "lima", "mike", "november", "oscar", "papa", "quebec", "romeo",
788            "sierra", "tango", "uniform", "victor", "whiskey", "xray", "yankee", "zulu",
789        ];
790
791        // filter out errors, since we can't sort_by on those
792        let mut values: Vec<Value> = Value::test_values()
793            .into_iter()
794            .filter(|val| !matches!(val, Value::Error { .. }))
795            .collect();
796
797        // reverse sort test values
798        values.sort_by(|a, b| b.partial_cmp(a).unwrap());
799
800        let mut list = values.clone();
801        let mut table: Vec<Value> = values
802            .clone()
803            .into_iter()
804            .map(|val| Value::test_record(record! { "value" => val }))
805            .collect();
806        let record = Record::from_iter(phonetic.into_iter().map(str::to_string).zip(values));
807
808        let comparator = Comparator::CellPath(CellPath {
809            members: vec![PathMember::String {
810                val: "value".to_string(),
811                span: Span::test_data(),
812                optional: false,
813                casing: Casing::Sensitive,
814            }],
815        });
816
817        assert!(sort(&mut list, false, false).is_ok());
818        assert!(
819            sort_by(
820                &mut table,
821                vec![comparator],
822                Span::test_data(),
823                false,
824                false
825            )
826            .is_ok()
827        );
828
829        let record_sorted = sort_record(record.clone(), true, false, false, false).unwrap();
830        let record_vals: Vec<Value> = record_sorted.into_iter().map(|pair| pair.1).collect();
831
832        let table_vals: Vec<Value> = table
833            .clone()
834            .into_iter()
835            .map(|record| record.into_record().unwrap().remove("value").unwrap())
836            .collect();
837
838        assert_eq!(list, record_vals);
839        assert_eq!(record_vals, table_vals);
840        // list == table_vals by transitive property
841    }
842}