Skip to content

Commit 9e4a76d

Browse files
committed
Populate sorting page
1 parent 8a179d6 commit 9e4a76d

File tree

1 file changed

+249
-2
lines changed

1 file changed

+249
-2
lines changed

book/sorting.md

Lines changed: 249 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Nushell offers many ways of sorting data, and which method you reach for will de
44

55
## Basic sorting
66

7+
### Lists
8+
79
Sorting a basic list works exactly how you might expect:
810

911
```nu
@@ -21,7 +23,7 @@ Sorting a basic list works exactly how you might expect:
2123
However, things get a bit more complex when you start combining types. For example, let's see what happens when we have a list with numbers _and_ strings:
2224

2325
```nu
24-
> ["foobar" 4 9 2 1 "hello" 8 6] | sort
26+
> ["hello" 4 9 2 1 "foobar" 8 6] | sort
2527
╭───┬────────╮
2628
│ 0 │ 1 │
2729
│ 1 │ 2 │
@@ -40,6 +42,251 @@ We can see that the numbers are sorted in order, and the strings are sorted to t
4042
If you _do_ want a sort containing differing types to error, see [strict sort](#strict-sort).
4143
:::
4244

43-
## Custom sorts
45+
Nushell's sort is also **stable**, meaning equal values will retain their original ordering relative to each other. This is illustrated here using the [insensitive](#insensitive-sort) sort option:
46+
47+
```nu
48+
> ["foo" "FOO" "BAR" "bar"] | sort -i
49+
╭───┬─────╮
50+
│ 0 │ BAR │
51+
│ 1 │ bar │
52+
│ 2 │ foo │
53+
│ 3 │ FOO │
54+
╰───┴─────╯
55+
```
56+
57+
Since this sort is case insensitive, `foo` and `FOO` are considered equal to each other, and the same is true for `bar` and `BAR`. In the result, the uppercase `BAR` precedes the lowercase `bar`, since the uppercase `BAR` also precedes the lowercase `bar` in the input. Similarly, the lowercase `foo` precedes the uppercase `FOO` in both the input and the result.
58+
59+
### Records
60+
61+
Records can be sorted two ways: by key, and by value. By default, passing a record to `sort` will sort in order of its keys:
62+
63+
```nu
64+
{x: 123, a: hello!, foo: bar} | sort
65+
╭─────┬────────╮
66+
│ a │ hello! │
67+
│ foo │ bar │
68+
│ x │ 123 │
69+
╰─────┴────────╯
70+
```
71+
72+
To instead sort in order of values, use the `-v` flag:
73+
74+
```nu
75+
{x: 123, a: hello! foo: bar} | sort -v
76+
╭─────┬────────╮
77+
│ x │ 123 │
78+
│ foo │ bar │
79+
│ a │ hello! │
80+
╰─────┴────────╯
81+
```
82+
83+
### Tables
84+
85+
Table rows are sorted by comparing rows by the columns in order. If two rows have equal values in their first column, they are sorted by their second column. This repeats until the rows are sorted different or all columns are equal.
86+
87+
```nu
88+
> let items = [
89+
{id: 100, price: 10, quantity: 5 }
90+
{id: 100, price: 5, quantity: 8 }
91+
{id: 100, price: 5, quantity: 1 }
92+
]
93+
> $items | sort
94+
╭───┬─────┬───────┬──────────╮
95+
│ # │ id │ price │ quantity │
96+
├───┼─────┼───────┼──────────┤
97+
│ 0 │ 100 │ 5 │ 1 │
98+
│ 1 │ 100 │ 5 │ 8 │
99+
│ 2 │ 100 │ 10 │ 5 │
100+
╰───┴─────┴───────┴──────────╯
101+
```
102+
103+
In this example, the `id` column for all items is equal. Then, the two items with price `5` are sorted before the item with price `10`. Finally, the `item` with quantity `1` is sorted before the item with quantity `8`.
104+
105+
## Sorting structured data
106+
107+
### Cell path
108+
109+
In order to sort more complex types, such as tables, you can use the `sort-by` command. `sort-by` can order its input by a [cell path](navigating_structured_data.html#cell-paths).
110+
111+
Here's an example directory, sorted by filesize:
112+
113+
```nu
114+
> ls | sort-by size
115+
╭───┬─────────────────────┬──────┬──────────┬────────────────╮
116+
│ # │ name │ type │ size │ modified │
117+
├───┼─────────────────────┼──────┼──────────┼────────────────┤
118+
│ 0 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
119+
│ 1 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
120+
│ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
121+
│ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
122+
╰───┴─────────────────────┴──────┴──────────┴────────────────╯
123+
```
124+
125+
We can also provide multiple cell paths to `sort-by`, which will sort by each cell path in order of priority. You can think of providing multiple cell paths as a "tiebreaker" for elements which have equal values. Let's sort first by size, then by modification time:
126+
127+
```nu
128+
> ls | sort-by size modified
129+
╭───┬─────────────────────┬──────┬──────────┬────────────────╮
130+
│ # │ name │ type │ size │ modified │
131+
├───┼─────────────────────┼──────┼──────────┼────────────────┤
132+
│ 0 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
133+
│ 1 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
134+
│ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
135+
│ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
136+
╰───┴─────────────────────┴──────┴──────────┴────────────────╯
137+
```
138+
139+
This time, `shopping_list.txt` comes before `my-secret-plans.txt`, since it has an earlier modification time, but two larger files remain sorted after the `.txt` files.
140+
141+
Furthermore, we use more complex cell paths to sort nested data:
142+
143+
```nu
144+
> let cities = [
145+
{name: 'New York', info: { established: 1624, population: 18_819_000 } }
146+
{name: 'Kyoto', info: { established: 794, population: 37_468_000 } }
147+
{name: 'São Paulo', info: { established: 1554, population: 21_650_000 } }
148+
]
149+
> $cities | sort-by info.established
150+
╭───┬───────────┬────────────────────────────╮
151+
│ # │ name │ info │
152+
├───┼───────────┼────────────────────────────┤
153+
│ 0 │ Kyoto │ ╭─────────────┬──────────╮ │
154+
│ │ │ │ established │ 794 │ │
155+
│ │ │ │ population │ 37468000 │ │
156+
│ │ │ ╰─────────────┴──────────╯ │
157+
│ 1 │ São Paulo │ ╭─────────────┬──────────╮ │
158+
│ │ │ │ established │ 1554 │ │
159+
│ │ │ │ population │ 21650000 │ │
160+
│ │ │ ╰─────────────┴──────────╯ │
161+
│ 2 │ New York │ ╭─────────────┬──────────╮ │
162+
│ │ │ │ established │ 1624 │ │
163+
│ │ │ │ population │ 18819000 │ │
164+
│ │ │ ╰─────────────┴──────────╯ │
165+
╰───┴───────────┴────────────────────────────╯
166+
```
167+
168+
### Sort by key closure
169+
170+
Sometimes, it's useful to sort data in a more complicated manner than "increasing" or "decreasing". Instead of using `sort-by` with a cell path, you can supply a [closure](types_of_data.html#closures), which will transform each value into a [sorting key](https://en.wikipedia.org/wiki/Collation#Sort_keys) _without changing the underlying data_. Here's an example of a key closure, where we want to sort a list of assignments by their average grade:
171+
172+
```nu
173+
> let assignments = [
174+
{name: 'Homework 1', grades: [97 89 86 92 89] }
175+
{name: 'Homework 2', grades: [91 100 60 82 91] }
176+
{name: 'Exam 1', grades: [78 88 78 53 90] }
177+
{name: 'Project', grades: [92 81 82 84 83] }
178+
]
179+
> $assignments | sort-by { get grades | math avg }
180+
╭───┬────────────┬───────────────────────╮
181+
│ # │ name │ grades │
182+
├───┼────────────┼───────────────────────┤
183+
│ 0 │ Exam 1 │ [78, 88, 78, 53, 90] │
184+
│ 1 │ Project │ [92, 81, 82, 84, 83] │
185+
│ 2 │ Homework 2 │ [91, 100, 60, 82, 91] │
186+
│ 3 │ Homework 1 │ [97, 89, 86, 92, 89] │
187+
╰───┴────────────┴───────────────────────╯
188+
```
189+
190+
The value is passed into the pipeline input of the key closure, however you can also use it as a parameter:
191+
192+
```nu
193+
> let weight = {alpha: 10, beta: 5, gamma: 3}
194+
> [alpha gamma beta gamma alpha] | sort-by {|val| $weight | get $val }
195+
╭───┬───────╮
196+
│ 0 │ gamma │
197+
│ 1 │ gamma │
198+
│ 2 │ beta │
199+
│ 3 │ alpha │
200+
│ 4 │ alpha │
201+
╰───┴───────╯
202+
```
203+
204+
### Sorting records
205+
206+
## Custom sort order
207+
208+
In addition to [key closures](#sort-by-key-closure), `sort-by` also supports closures which specify a custom sort order. The `--custom`, or `-c`, flag will tell `sort-by` to interpret closures as custom sort closures. A custom sort closure has two parameters, and returns a boolean. The closure should return `true` if the first parameter comes _before_ the second parameter in the sort order.
209+
210+
For a simple example, we could rewrite a cell path sort as a custom sort:
211+
212+
```nu
213+
> ls | sort-by -c {|a, b| $a.size < $b.size }
214+
╭───┬─────────────────────┬──────┬──────────┬────────────────╮
215+
│ # │ name │ type │ size │ modified │
216+
├───┼─────────────────────┼──────┼──────────┼────────────────┤
217+
│ 0 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
218+
│ 1 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
219+
│ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
220+
│ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
221+
╰───┴─────────────────────┴──────┴──────────┴────────────────╯
222+
```
223+
224+
::: tip
225+
The parameters are also passed to the custom closure as a two element list, so the following are equivalent:
226+
227+
- `{|a, b| $a < $b }`
228+
- `{ $in.0 < $in.1 }`
229+
:::
230+
231+
Here's an example of a custom sort which couldn't be trivially written as a key sort. In this example, we have a queue of tasks with some amount of work time and a priority. We want to sort by priority (highest first), but if a task has had zero work time, we want to schedule it immediately. Otherwise, we ignore the work time.
232+
233+
```nu
234+
> let queue = [
235+
{task: 139, work_time: 0, priority: 1 }
236+
{task: 52, work_time: 355, priority: 8 }
237+
{task: 948, work_time: 72, priority: 2 }
238+
{task: 583, work_time: 0, priority: 5 }
239+
]
240+
> let my_sort = {|a, b|
241+
if ($a.work_time == 0) {
242+
true
243+
} else if ($b.work_time == 0) {
244+
false
245+
} else {
246+
$a.priority > $b.priority
247+
}
248+
}
249+
> $queue | sort-by -c $my_sort
250+
╭───┬──────┬───────────┬──────────╮
251+
│ # │ task │ work_time │ priority │
252+
├───┼──────┼───────────┼──────────┤
253+
│ 0 │ 583 │ 0 │ 5 │
254+
│ 1 │ 139 │ 0 │ 1 │
255+
│ 2 │ 52 │ 355 │ 8 │
256+
│ 3 │ 948 │ 72 │ 2 │
257+
╰───┴──────┴───────────┴──────────╯
258+
```
44259

45260
### Strict sort
261+
262+
Custom sort closures also provide a simple way to sort data while enforcing type homogeneity. This takes advantage of [operators requiring compatible data types](operators.html#types):
263+
264+
```nu
265+
> let data = ["hello" 4 9 2 1 "foobar" 8 6]
266+
> $data | sort-by -c {|a, b| $a < $b}
267+
Error: nu::shell::type_mismatch
268+
269+
× Type mismatch during operation.
270+
╭─[entry #173:1:28]
271+
1 │ $data | sort-by -c {|a, b| $a < $b}
272+
· ─┬ ┬ ─┬
273+
· │ │ ╰── string
274+
· │ ╰── type mismatch for operator
275+
· ╰── int
276+
╰────
277+
278+
```
279+
280+
::: warning
281+
This does not currently work with `null` values due to comparison between any value and `null` returning `null`.
282+
:::
283+
284+
## Special sorts
285+
286+
### Insensitive sort
287+
288+
### Natural sort
289+
290+
### Sorting with mixed types
291+
292+
Under some circumstances, you might end up with values containing mixed types.

0 commit comments

Comments
 (0)