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

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

★프로그래밍/Ruby :: 2016.08.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 12    Yday 63    Tot 65,494
Juwan Park
Juwan Park's blog is powered by Daum and TISTORY.
Contemporary Blue for TISTORY.
Designed by Juwan Park. Creative Commons License
▲ TOP