정렬
누셸은 데이터를 정렬하는 여러 가지 방법을 제공하며, 어떤 방법을 사용할지는 문제와 작업 중인 데이터 종류에 따라 다릅니다. 데이터를 정렬하려는 몇 가지 방법을 살펴보겠습니다.
기본 정렬
목록
기본 목록을 정렬하는 것은 예상대로 작동합니다.
[9 3 8 1 4 6] | sort
# => ╭───┬───╮
# => │ 0 │ 1 │
# => │ 1 │ 3 │
# => │ 2 │ 4 │
# => │ 3 │ 6 │
# => │ 4 │ 8 │
# => │ 5 │ 9 │
# => ╰───┴───╯
그러나 형식을 결합하기 시작하면 상황이 조금 더 복잡해집니다. 예를 들어, 숫자 와 문자열이 포함된 목록이 있는 경우 어떻게 되는지 보겠습니다.
["hello" 4 9 2 1 "foobar" 8 6] | sort
# => ╭───┬────────╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 4 │
# => │ 3 │ 6 │
# => │ 4 │ 8 │
# => │ 5 │ 9 │
# => │ 6 │ foobar │
# => │ 7 │ hello │
# => ╰───┴────────╯
숫자는 순서대로 정렬되고 문자열은 목록 끝으로 정렬되며 순서대로 정렬되는 것을 볼 수 있습니다. 다른 프로그래밍 언어에서 온 경우 이것이 예상과 다를 수 있습니다. 누셸에서는 일반적으로 데이터는 항상 오류 없이 정렬될 수 있습니다.
팁
다른 유형이 포함된 정렬이 오류를 발생시키기를 원하는 경우 엄격한 정렬을 참조하십시오.
누셸의 정렬은 또한 안정적이므로 동일한 값은 서로에 대한 원래 순서를 유지합니다. 이것은 대소문자를 구분하지 않는 정렬 옵션을 사용하여 여기에 설명되어 있습니다.
["foo" "FOO" "BAR" "bar"] | sort -i
# => ╭───┬─────╮
# => │ 0 │ BAR │
# => │ 1 │ bar │
# => │ 2 │ foo │
# => │ 3 │ FOO │
# => ╰───┴─────╯
이 정렬은 대소문자를 구분하지 않으므로 foo
와 FOO
는 서로 동일한 것으로 간주되며 bar
와 BAR
도 마찬가지입니다. 결과적으로 대문자 BAR
는 입력에서 대문자 BAR
가 소문자 bar
앞에 오기 때문에 소문자 bar
앞에 옵니다. 마찬가지로 소문자 foo
는 입력과 결과 모두에서 대문자 FOO
앞에 옵니다.
레코드
레코드는 키와 값의 두 가지 방법으로 정렬할 수 있습니다. 기본적으로 레코드를 sort
에 전달하면 키 순서로 정렬됩니다.
{x: 123, a: hello!, foo: bar} | sort
# => ╭─────┬────────╮
# => │ a │ hello! │
# => │ foo │ bar │
# => │ x │ 123 │
# => ╰─────┴────────╯
대신 값 순서로 정렬하려면 -v
플래그를 사용하십시오.
{x: 123, a: hello! foo: bar} | sort -v
# => ╭─────┬────────╮
# => │ x │ 123 │
# => │ foo │ bar │
# => │ a │ hello! │
# => ╰─────┴────────╯
테이블
테이블 행은 열 순서대로 행을 비교하여 정렬됩니다. 두 행의 첫 번째 열 값이 같으면 두 번째 열로 정렬됩니다. 행이 다르게 정렬되거나 모든 열이 같을 때까지 반복됩니다.
let items = [
{id: 100, quantity: 10, price: 5 }
{id: 100, quantity: 5, price: 8 }
{id: 100, quantity: 5, price: 1 }
]
$items | sort
# => ╭───┬─────┬──────────┬───────╮
# => │ # │ id │ quantity │ price │
# => ├───┼─────┼──────────┼───────┤
# => │ 0 │ 100 │ 5 │ 1 │
# => │ 1 │ 100 │ 5 │ 8 │
# => │ 2 │ 100 │ 10 │ 5 │
# => ╰───┴─────┴──────────┴───────╯
이 예제에서 모든 항목의 id
열은 동일합니다. 그런 다음 수량이 5
인 두 항목이 수량이 10
인 항목보다 먼저 정렬됩니다. 마지막으로 가격이 1
인 item
이 가격이 8
인 항목보다 먼저 정렬됩니다.
구조화된 데이터 정렬
셀 경로
테이블과 같은 더 복잡한 유형을 정렬하려면 sort-by
명령을 사용할 수 있습니다. sort-by
는 셀 경로별로 입력을 정렬할 수 있습니다.
다음은 파일 크기별로 정렬된 예제 디렉터리입니다.
ls | sort-by size
# => ╭───┬─────────────────────┬──────┬──────────┬────────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼─────────────────────┼──────┼──────────┼────────────────┤
# => │ 0 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
# => │ 1 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
# => │ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
# => │ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
# => ╰───┴─────────────────────┴──────┴──────────┴────────────────╯
sort-by
에 여러 셀 경로를 제공할 수도 있으며, 그러면 우선 순위 순서대로 각 셀 경로별로 정렬됩니다. 여러 셀 경로를 제공하는 것을 값이 같은 요소에 대한 "동점자"로 생각할 수 있습니다. 먼저 크기별로 정렬한 다음 수정 시간별로 정렬해 보겠습니다.
ls | sort-by size modified
# => ╭───┬─────────────────────┬──────┬──────────┬────────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼─────────────────────┼──────┼──────────┼────────────────┤
# => │ 0 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
# => │ 1 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
# => │ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
# => │ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
# => ╰───┴─────────────────────┴──────┴──────────┴────────────────╯
이번에는 shopping_list.txt
가 my-secret-plans.txt
보다 수정 시간이 더 빠르기 때문에 먼저 나오고, 두 개의 더 큰 파일은 .txt
파일 뒤에 정렬된 상태로 유지됩니다.
또한 더 복잡한 셀 경로를 사용하여 중첩된 데이터를 정렬할 수 있습니다.
let cities = [
{name: 'New York', info: { established: 1624, population: 18_819_000 } }
{name: 'Kyoto', info: { established: 794, population: 37_468_000 } }
{name: 'São Paulo', info: { established: 1554, population: 21_650_000 } }
]
$cities | sort-by info.established
# => ╭───┬───────────┬────────────────────────────╮
# => │ # │ name │ info │
# => ├───┼───────────┼────────────────────────────┤
# => │ 0 │ Kyoto │ ╭─────────────┬──────────╮ │
# => │ │ │ │ established │ 794 │ │
# => │ │ │ │ population │ 37468000 │ │
# => │ │ │ ╰─────────────┴──────────╯ │
# => │ 1 │ São Paulo │ ╭─────────────┬──────────╮ │
# => │ │ │ │ established │ 1554 │ │
# => │ │ │ │ population │ 21650000 │ │
# => │ │ │ ╰─────────────┴──────────╯ │
# => │ 2 │ New York │ ╭─────────────┬──────────╮ │
# => │ │ │ │ established │ 1624 │ │
# => │ │ │ │ population │ 18819000 │ │
# => │ │ │ ╰─────────────┴──────────╯ │
# => ╰───┴───────────┴────────────────────────────╯
키 클로저로 정렬
때로는 "증가" 또는 "감소"보다 더 복잡한 방식으로 데이터를 정렬하는 것이 유용합니다. 셀 경로와 함께 sort-by
를 사용하는 대신 클로저를 제공할 수 있으며, 이는 기본 데이터를 변경하지 않고 각 값을 정렬 키로 변환합니다. 다음은 평균 성적별로 과제 목록을 정렬하려는 키 클로저의 예입니다.
let assignments = [
{name: 'Homework 1', grades: [97 89 86 92 89] }
{name: 'Homework 2', grades: [91 100 60 82 91] }
{name: 'Exam 1', grades: [78 88 78 53 90] }
{name: 'Project', grades: [92 81 82 84 83] }
]
$assignments | sort-by { get grades | math avg }
# => ╭───┬────────────┬───────────────────────╮
# => │ # │ name │ grades │
# => ├───┼────────────┼───────────────────────┤
# => │ 0 │ Exam 1 │ [78, 88, 78, 53, 90] │
# => │ 1 │ Project │ [92, 81, 82, 84, 83] │
# => │ 2 │ Homework 2 │ [91, 100, 60, 82, 91] │
# => │ 3 │ Homework 1 │ [97, 89, 86, 92, 89] │
# => ╰───┴────────────┴───────────────────────╯
값은 키 클로저의 파이프라인 입력으로 전달되지만 매개변수로 사용할 수도 있습니다.
let weight = {alpha: 10, beta: 5, gamma: 3}
[alpha gamma beta gamma alpha] | sort-by {|val| $weight | get $val }
# => ╭───┬───────╮
# => │ 0 │ gamma │
# => │ 1 │ gamma │
# => │ 2 │ beta │
# => │ 3 │ alpha │
# => │ 4 │ alpha │
# => ╰───┴───────╯
사용자 지정 정렬 순서
키 클로저 외에도 sort-by
는 사용자 지정 정렬 순서를 지정하는 클로저도 지원합니다. --custom
또는 -c
플래그는 sort-by
에게 클로저를 사용자 지정 정렬 클로저로 해석하도록 지시합니다. 사용자 지정 정렬 클로저에는 두 개의 매개변수가 있으며 부울을 반환합니다. 클로저는 첫 번째 매개변수가 정렬 순서에서 두 번째 매개변수보다 앞에 오면 true
를 반환해야 합니다.
간단한 예로, 셀 경로 정렬을 사용자 지정 정렬로 다시 작성할 수 있습니다. 이것은 "$a.size
가 $b.size
보다 작으면 정렬 순서에서 a
가 b
보다 앞에 와야 합니다"라고 읽을 수 있습니다.
ls | sort-by -c {|a, b| $a.size < $b.size }
# => ╭───┬─────────────────────┬──────┬──────────┬────────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼─────────────────────┼──────┼──────────┼────────────────┤
# => │ 0 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
# => │ 1 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
# => │ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
# => │ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
# => ╰───┴─────────────────────┴──────┴──────────┴────────────────╯
팁
매개변수는 두 요소 목록으로 사용자 지정 클로저에 전달되므로 다음은 동일합니다.
{|a, b| $a < $b }
{ $in.0 < $in.1 }
:::
다음은 키 정렬로 간단하게 작성할 수 없는 사용자 지정 정렬의 예입니다. 이 예제에서는 작업 시간과 우선 순위가 있는 작업 대기열이 있습니다. 우선 순위(가장 높은 순서)별로 정렬하고 싶습니다. 작업 시간이 0인 작업이 있으면 즉시 예약하고, 그렇지 않으면 작업 시간을 무시합니다.
let queue = [
{task: 139, work_time: 0, priority: 1 }
{task: 52, work_time: 355, priority: 8 }
{task: 948, work_time: 72, priority: 2 }
{task: 583, work_time: 0, priority: 5 }
]
let my_sort = {|a, b|
match [$a.work_time, $b.work_time] {
[0, 0] => ($a.priority > $b.priority) # 작업 시간이 같으면 우선 순위로 대체
[0, _] => true, # a만 작업 시간이 0이므로 정렬 순서에서 a가 b보다 앞에 옴
[_, 0] => false, # b만 작업 시간이 0이므로 정렬 순서에서 a가 b보다 뒤에 옴
_ => ($a.priority > $b.priority) # 둘 다 0이 아닌 작업 시간을 가지므로 우선 순위로 정렬
}
}
$queue | sort-by -c $my_sort
특수 정렬
대소문자를 구분하지 않는 정렬
대소문자를 구분하지 않는 정렬을 사용할 때 대소문자만 다른 문자열(및 glob)은 정렬에 대해 동일한 것으로 간주되는 반면 다른 유형은 영향을 받지 않습니다.
let data = [
Nushell,
foobar,
10,
nushell,
FoOBaR,
9
]
$data | sort -i
# => ╭───┬─────────╮
# => │ 0 │ 9 │
# => │ 1 │ 10 │
# => │ 2 │ foobar │
# => │ 3 │ FoOBaR │
# => │ 4 │ Nushell │
# => │ 5 │ nushell │
# => ╰───┴─────────╯
자연 정렬
자연 정렬 옵션을 사용하면 숫자가 포함된 문자열을 숫자가 일반적으로 정렬되는 것과 같은 방식으로 정렬할 수 있습니다. 이것은 숫자로만 구성된 문자열과 숫자와 문자가 있는 문자열 모두에 대해 작동합니다.
let data = ["10", "9", "foo123", "foo20", "bar123", "bar20"]
$data | sort
# => ╭───┬────────╮
# => │ 0 │ 10 │
# => │ 1 │ 9 │
# => │ 2 │ bar123 │
# => │ 3 │ bar20 │
# => │ 4 │ foo123 │
# => │ 5 │ foo20 │
# => ╰───┴────────╯
# "1"은 "9"보다 먼저 정렬되므로 "10"은 "9"보다 먼저 정렬됩니다.
$data | sort -n
# => ╭───┬────────╮
# => │ 0 │ 9 │
# => │ 1 │ 10 │
# => │ 2 │ bar20 │
# => │ 3 │ bar123 │
# => │ 4 │ foo20 │
# => │ 5 │ foo123 │
# => ╰───┴────────╯
또한 자연 정렬을 사용하면 숫자와 숫자 문자열을 함께 정렬할 수 있습니다.
let data = [4, "6.2", 1, "10", 2, 8.1, "3", 5.5, "9", 7]
$data | sort -n
# => ╭───┬──────╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => │ 4 │ 5.50 │
# => │ 5 │ 6.2 │
# => │ 6 │ 7 │
# => │ 7 │ 8.10 │
# => │ 8 │ 9 │
# => │ 9 │ 10 │
# => ╰───┴──────╯
혼합 유형으로 정렬
어떤 상황에서는 혼합 유형이 포함된 데이터를 정렬해야 할 수 있습니다. 혼합 유형을 정렬할 때 알아야 할 몇 가지 사항이 있습니다.
- 일반적으로 동일한 유형의 값은 정렬 순서에서 서로 옆에 표시됩니다. 예를 들어, 정렬된 숫자가 먼저 오고, 그 다음 정렬된 문자열, 그 다음 정렬된 목록이 옵니다.
- 일부 유형은 정렬 순서에서 혼합됩니다. 다음과 같습니다.
- 정수 및 부동 소수점. 예를 들어
[2.2, 1, 3]
은[1, 2.2, 3]
으로 정렬됩니다. - 문자열 및 glob. 예를 들어
[("b" | into glob) a c]
는[a b c]
로 정렬됩니다(여기서 b는 여전히 glob임). - 자연 정렬을 사용하는 경우 정수, 부동 소수점 및 문자열이 해당 섹션에 설명된 대로 혼합됩니다.
- 정수 및 부동 소수점. 예를 들어
- 혼합되지 않은 유형 간의 순서는
null
값을 제외하고 보장되지 않으며, 이는 항상 목록의 끝으로 정렬됩니다.- 동일한 누셸 버전 내에서 순서는 항상 동일해야 하지만 이에 의존해서는 안 됩니다. 유형 간의 순서에 민감한 코드가 있는 경우 요구 사항을 더 잘 표현하는 사용자 지정 정렬을 사용하는 것을 고려하십시오.
혼합 유형이 포함될 수 있는 데이터를 정렬해야 하는 경우 다음 전략 중 하나를 고려하십시오.
- 엄격한 정렬을 사용하여 호환되지 않는 유형의 정렬을 허용하지 않습니다.
- 자연 정렬을 사용하여 혼합된 숫자와 숫자 문자열을 정렬합니다.
to text
,to nuon
또는to json
을 적절하게 사용하는 키 정렬describe
를 사용하여 유형을 명시적으로 확인하는 사용자 지정 정렬
엄격한 정렬
사용자 지정 정렬 클로저는 잘 정의된 비교가 있는 유형만 함께 정렬되도록 하면서 데이터를 정렬하는 간단한 방법을 제공합니다. 이것은 호환되는 데이터 유형을 요구하는 연산자를 활용합니다.
let compatible = [8 3.2 null 58 2]
let incompatible = ["hello" 4 9 2 1 "meow" 8 6]
$compatible | sort-by -c {|a, b| $a < $b | default ($a != null) }
# => ╭───┬──────╮
# => │ 0 │ 2 │
# => │ 1 │ 3.20 │
# => │ 2 │ 8 │
# => │ 3 │ 58 │
# => │ 4 │ │
# => ╰───┴──────╯
$incompatible | sort-by -c {|a, b| $a < $b | default ($a != null) }
# => Error: nu::shell::type_mismatch
# =>
# => × Type mismatch during operation.
# => ╭─[entry #26:1:36]
# => 1 │ $incompatible | sort-by -c {|a, b| $a < $b | default ($a != null) }
# => · ─┬ ┬ ─┬
# => · │ │ ╰── string
# => · │ ╰── type mismatch for operator
# => · ╰── int
# => ╰────
null
값은 모든 값과 null
간의 비교가 null
을 반환하므로 특별한 처리가 필요합니다. 대신 null
값을 거부하려면 다음을 시도하십시오.
let baddata = [8 3.2 null 58 2]
let strict = {|a, b|
match [$a, $b] {
[null, _] => (error make {msg: "Attempt to sort null"}),
[_, null] => (error make {msg: "Attempt to sort null"}),
_ => ($a < $b)
}
}
$baddata | sort-by -c $strict
# => Error: × Attempt to sort null
# => ╭─[entry #3:4:21]
# => 3 │ match [$a, $b] {
# => 4 │ [null, _] => (error make {msg: "Attempt to sort null"}),
# => · ─────┬────
# => · ╰── originates from here
# => 5 │ [_, null] => (error make {msg: "Attempt to sort null"}),
# => ╰────