Juwan Park :: .sort - 배열의 값을 정렬하는 메소드

.sort - 배열의 값을 정렬하는 메소드

★프로그래밍/Ruby :: 2016. 8. 24. 00:03

루비에서 사용하는 배열에는 특징이 있습니다.

그 특징들 중 하나로, 자체적인 정렬 메소드를 제공한다는 점입니다.

흔히 정렬 하면 정렬 알고리즘을 코드에 짜 넣는 경우가 일반적이지만, 루비에서는 정렬 메소드를 제공함으로써 배열의 정렬 알고리즘을 코딩하는 수고를 덜 수 있습니다.

일단 다음 코드를 봅시다.

arr = [4, 1, 2, 5, 3]
(arr.length - 2).downto(0) do |x|
  0.upto(x) do |y|
    arr[y], arr[y+1] = arr[y+1], arr[y] if arr[y] > arr[y+1]
  end
end
puts sprintf("%s", arr)

이 코드를 실행하면 맨 처음 [4, 1, 2, 5, 3]으로 세팅된 배열 arr를 오름차순 정렬하여 [1, 2, 3, 4, 5]로 만들고 화면에 출력하게 됩니다.

가장 간단하다는 버블 정렬로 코딩했지만 5줄을 차지했네요. 이를 한 줄로 줄일 수 있을까요?
2번 줄부터 6번 줄까지를 붙여서 한 줄로 줄일 수는 있습니다. 하지만 이러면 코드를 읽기 힘들어지는 문제가 생기겠지요.

arr = [4, 1, 2, 5, 3]
puts sprintf("%s", arr.sort)

이렇게 줄였습니다. 저 앞의 2번 줄부터 6번 줄까지의 5줄을 삭제하고 arr 뒤에 .sort만 붙였을 뿐입니다. 하지만 이 코드 역시 [1, 2, 3, 4, 5]를 화면에 출력합니다. 왜냐하면, 배열 뒤에 붙은 sort 메소드가 자체적으로 배열을 정렬해 주는 메소드이기 때문입니다.

이와 같이 루비에서는 정렬 알고리즘을 코딩하지 않고도 자체적으로 정렬 기능이 지원됩니다.

아 참, .sort 메소드도 뒤에 느낌표가 붙는 것이 있고 안 붙는 것이 있습니다. 루비에서 문자열 치환하기 포스트에서 .gsub 메소드에 느낌표가 있고의 차이를 설명했는데, .sort 뒤의 느낌표 유무도 마찬가지입니다. 다음 코드를 봅시다.

arr = [4, 1, 2, 5, 3]
puts sprintf("%s", arr.sort)
puts sprintf("%s", arr)
puts sprintf("%s", arr.sort!)
puts sprintf("%s", arr)

이 코드의 실행 결과는 다음과 같을 것입니다.

[1, 2, 3, 4, 5]
[4, 1, 2, 5, 3]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

gsub 뒤의 느낌표 유무와 마찬가지로, sort 뒤에 느낌표를 안 붙이면 정렬만 수행하고 그 정렬 결과는 원래 배열에 저장되지 않습니다.
여기서는 sort 뒤에 느낌표를 안 붙여서 일단 [1, 2, 3, 4, 5]로 정렬된 상태로 출력하지만 바로 다음에 sort 메소드 없이 출력하게 되면 정렬되기 전인 [4, 1, 2, 5, 3] 순서를 출력하게 됩니다.
하지만 sort 뒤에 느낌표를 붙이면 [1, 2, 3, 4, 5]로 정렬함과 동시에 그 값이 원래 배열인 arr에 그대로 저장됩니다. 그래서 바로 다음에 sort 메소드 없이 출력하더라도 정렬된 상태인 [1, 2, 3, 4, 5]로 출력되는 것입니다.

그렇다면, sort 메소드로는 오름차순 정렬만 가능한 것일까요? 아닙니다. 내림차순 정렬도 가능합니다.

arr = [4, 1, 2, 5, 3]
puts sprintf("%s", arr.sort)
puts sprintf("%s", arr.sort {|x,y| y <=> x})

이를 실행한 결과는 다음과 같습니다.

[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]

그냥 sort 메소드만 쓰면 오름차순 출력이 되어 [1, 2, 3, 4, 5]로 출력되지만, sort 메소드 뒤에 {|x, y| y <=> x} 추가를 해 주면 반대로 내림차순이 되어 [5, 4, 3, 2, 1]로 출력됩니다. {|x, y| y <=> x} 대신 .reverse 메소드를 추가로 붙여 .sort.reverse로 써도 같은 결과가 발생합니다.

정렬은 숫자 뿐만 아니라 문자열이 든 배열에도 사용할 수 있습니다.

arr = ["홍길동", "이순신", "강감찬", "김유신", "이황", "이이"]
arr.sort!

이를 실행하면 배열 arr는 ["강감찬", "김유신", "이순신", "이이", "이황", "홍길동"] 순서가 됩니다.

문자열 정렬은 로마자, 한글, 한자 등이 혼용되어도 가능합니다. 이 경우 문자 코드 순서로 정렬됩니다.

arr = ["가", "あ", "漢", "A", "Ж", "A"]
arr.sort!

이를 실행하면 배열 arr는 ["A", "Ж", "あ", "漢", "가", "A"] 순서가 됩니다. (단, 인코딩은 유니코드 UTF-8이라고 가정)

주의할 점은, 정렬을 실행할 배열 안의 자료형이 모두 같아야 한다는 점입니다. 즉, 숫자로만 이루어진 배열이나 문자열로만 이루어진 배열은 정상적으로 정렬되지만, 숫자와 문자열이 섞인 배열 등은 오류를 일으킵니다.

# 다음과 같은 경우 정상적으로 실행됩니다.
  [1, -2, -3].sort                        # [-3, -2, 1]
  ["소원", "예린", "은하", "유주", "신비", "엄지"].sort
     # ["소원", "신비", "엄지", "예린", "유주", "은하"]

# 다음과 같은 경우 오류를 일으킵니다.
  [1, 2, "삼", "넷", "오"].sort

또한, 배열 안의 배열끼리도 정렬이 가능합니다. 다만, 배열끼리 정렬할 경우 그 배열들 안의 자료형이 모두 같지 않아도 상관없으나 그 배열들끼리 자료형의 순서가 모두 같아야 합니다.
예를 들어, 정렬하려는 배열이 [[1, "나", "C"], [3, "가", "B"]]처럼 모두 [숫자, 문자열, 문자열] 식으로 구성된 경우 등은 정상적으로 배열되지만 [[1, "가", 2], [2, 3, "나"]]와 같이 하나는 [숫자, 문자열, 숫자], 다른 하나는 [숫자, 숫자, 문자열] 식으로 섞이게 되면 경우에 따라 오류를 일으킬 수 있습니다. 그 이유는 다음에 설명합니다.

# 다음과 같은 경우 정상적으로 실행됩니다.
  [[1, 2], [6, 5], [4, 3, 7]].sort           # [[1, 2], [4, 3, 7], [6, 5]]
  [["E", "D"], ["A", "F"], ["C", "B"]].sort  # [["A", "F", "G"], ["C", "B"], ["E", "D"]]
  [[2, "가"], [3, "나"], [1, "다"]].sort     # [[1, "다"], [2, "가"], [3, "나"]]
  [[2, "가"], [3, 4], [1, "다"]].sort        # [[1, "다"], [2, "가"], [3, 4]]

# 다음과 같은 경우 오류를 일으킵니다.
  [["여기", 1, 2], ["거기", 4, 4], ["거기", 4, "응"]].sort


배열끼리 정렬하면 정렬 순서가 궁금해지실 분이 계실 것입니다.

만약 배열끼리 정렬한다면 다음과 같은 순서입니다.

  1. 각 배열들의 0번 원소의 값을 비교하여 정렬
  2. 0번 원소의 값이 같을 경우 그 배열들끼리 1번 원소의 값을 비교하여 정렬
  3. 1번 원소의 값도 같을 경우 그 배열들끼리 2번 원소의 값을 비교하여 정렬하고 2번 원소도 같을 경우 3번 원소를 비교하는 식으로 반복

예를 들어,

[[1, 5, 6], [2, 3, 4], [1, 4, 7]].sort
arr.sort!

이 코드는 arr 값이 [[1, 4, 7], [1, 5, 6], [2, 3, 4]] 이런 순서가 됩니다. 먼저 정렬할 배열들의 0번 원소를 비교해서 1인 두 배열이 앞에, 2인 한 배열이 뒤로 갑니다. 그리고 0번 원소가 1이라 앞으로 간 두 배열끼리 1번 원소를 비교하는데, 1번 원소가 4인 배열이 5인 배열보다 앞으로 가게 되어 이런 결과가 나옵니다. 여기서 정렬이 끝나면, 2번 원소끼리는 값 비교를 생략합니다. 그래서 숫자와 문자열이 섞인 다차원 배열의 경우 값 비교를 위해 다음 원소로 넘기다가 자료형 충돌이 발생하는 순간 오류를 일으키는 것입니다. 다만 자료형 충돌이 일어나기 전에 정렬이 먼저 끝나면 오류 없이 지나갑니다.

그러면 원소 수가 다른 배열끼리는 어떻게 정렬될까요?

[[1, 2, -3], [1, 1], [1, -1, 4]].sort
arr.sort!

다른 배열은 원소가 3개인데 가운데 하나만 원소가 2개입니다. 즉, 나머지 두 배열은 2번 원소가 있지만 가운데 하나는 2번 원소가 없습니다.
그런데도 arr 값은 [[1, -1, 4], [1, 1], [1, 2, -3]] 이런 순서로 1번 원소의 값대로 정렬되었습니다. 이는 일단 0번 원소의 값이 다 같아서 1번 원소로 넘겼는데 1번 원소의 값이 다 다르기에 거기서 정렬이 이미 끝나 2번 원소부터는 정렬이 되지 않기 때문입니다.

만약에

[[1, 2, 4], [1, 2], [1, 2, -3], [1, 2]].sort
arr.sort!

이렇게 되면 2번 원소까지 비교해야 하는 상황인데 두 배열은 2번 원소가 있고 다른 두 배열은 2번 원소가 없습니다. 이러면 어떻게 될까요?
[[1, 2], [1, 2], [1, 2, -3], [1, 2, 4]] 이 순서가 됩니다. 일단 2번 원소가 없는 [1, 2] 배열 두개를 맨 앞으로 보낸 다음 나머지 두 배열의 2번 원소인 4와 -3을 비교합니다. 오름차순에 의하면 -3이 4보다 앞이므로 [1, 2, -3]이 [1, 2, 4]보다 앞에 옵니다.
만약 내림차순 정렬이라면 반대로 [[1, 2, 4], [1, 2, -3], [1, 2], [1, 2]] 순서가 됩니다.


여기서 루비 배열의 sort 메소드에 대한 설명을 마칩니다.

루비에서는 이런 방법으로 데이터 정렬을 편리하게 처리할 수 있습니다.

많은 도움 되셨나요?

유용한 정보로 활용하시기를 바랍니다.

Today    Yday    Tot
Juwan Park
Juwan Park's blog is powered by Daum and .
Contemporary Blue for .
Designed by Juwan Park. Creative Commons License
▲ TOP