golang의 sort패키지 사용해보기

By | 2016/06/24

[markdown]
golang에 sort라는 패키지가 있을 것이다라는 생각은 했지만, 잘 되어 있을거라는 생각은 못했는데,왠걸 내가 구현한것 보다 훨씬 잘 되어 있어서 내가만든거는 버리기로 했다.
내가 원하는 기능의 리스트는 아래와 같았다.

– 구조체 안에 있는 점수를 비교해서 구조체 리스트를 정렬하고 싶다.
– 점수가 같은 경우에는 나중에 업데이트된 녀석에게 상위 등급을 주고 싶다.
– 역순으로 정렬 하면 좋겠다.

결론적으로는 내가 원하는 기능보다 강력한 기능이 go의 sort패키지에 탑재 되어 있었다.
일단 정렬을 하려면 인터페이스를 만족시켜줘야 하는데, go의 인터페이스의 구현은 직접적으로 구현을 하는 것이 아니라,
그냥 해당 인터페이스가 제공하는 메서드를 모두 가지고 있으면, 그 인터페이스를 구현한것이다 라고 하기 때문에 sort패키지의 인터페이스를 단순히 구현을 하면 된다.
int, float, string은 go를 만드신 분들이 만들어 놨기 때문에, 그냥 쓸 수 있다. 그리고 내가 만든 구조체를 정렬하기 위해서는 구조체를 정렬하기 위한 타입을 만들어서 인터페이스의 메서드들을 구현해 주면 된다~!
말로하니깐 긴데 코드는 그렇게 길지 않다.

“`
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
“`

sort인터페이스코드인데 Len, Less, Swap 세가지 메서드를 구현하면 sort.Interface가 되는 것이되겠다.

### 숫자 정렬

일단 그냥 숫자를 정렬해보자.
import 관련된 코드는 그냥 생략 하도록 하겠다.

“`
func sortInt() {
// 램덤한 수를 세팅하기 위해 유닉스 시간으로 Seed를 저장
// 리스트의 숫자는 30개로 지정한다.
rand.Seed(time.Now().UTC().UnixNano())
count := 30
maxInt := 10000
arr := make([]int , count)

for i, _ := range arr {
arr[i] = rand.Int() % int(maxInt)
}

fmt.Println(“Sort this array : “, arr)
sort.Sort(sort.IntSlice(arr))
fmt.Println(“Sorted result : “, arr)
sort.Sort(sort.Reverse(sort.IntSlice(arr)))
fmt.Println(“Reversed result : “, arr)
}
“`

실행하면 이런 식으로 나올 것이다.

“`
Sort this array : [8298 6378 8489 5548 734 6301 6347 2620 2636 2569 3039 168 2130 8599 9316 723 4642 6531 147 42 188 264 5544 260 5101 5595 550 6594 9128 3430]
Sorted result : [42 147 168 188 260 264 550 723 734 2130 2569 2620 2636 3039 3430 4642 5101 5544 5548 5595 6301 6347 6378 6531 6594 8298 8489 8599 9128 9316]
Reversed result : [9316 9128 8599 8489 8298 6594 6531 6378 6347 6301 5595 5548 5544 5101 4642 3430 3039 2636 2620 2569 2130 734 723 550 264 260 188 168 147 42]
“`

### 구조체 정렬

숫자는 해봤으니, 이제 좀 더 실제적으로 사용할 만한 녀석으로 구조체를 정렬 해보자. 내가 하고자 하는것은 카카오 게임에서 랭킹을 만들어주는 역활을 하는 녀석이다.
구조체는 이런식으로 생겼다. 더 많은 데이터가 있지만, 공부에 필요없는 녀석은 생략했다.

“`
type Ranking struct {
kakaoId string
score int
written_date int // unix timestamp
}

// 테스트 결과를 이뿌게 보기위해 String 함수를 추가하였다.
func (ranking Ranking) String () string {
return fmt.Sprintf(“kakaoid %s score %d written_date %d\n”, ranking.kakaoid, ranking.score, ranking.written_date)
}
“`

그럼 이제부터 Ranking 구조체를 위한 예제 코드를 만들어보자.
아래 코드는 sort.interface를 구현 하기 위한 코드들이다. 소팅에 사용할 RankingSlice 타입을 새로 정의 하였다.
그리고 `Len, Less, Swap` 세개의 함수를 만들어 주었다.
점수가 큰녀석이 상위 랭크가 되도록 하였고, 점수가 같은 경우는 written_date가 큰녀석이 상위랭크가 되도록 작성했다.

“`
type RankingSlice []Ranking
func (arr RankingSlice) Len() int {
return len(arr)
}

// j 인덱스를 가진녀석이 i 앞으로 와야하는지 말아야하는지를 판단하는 함수
func (arr RankingSlice) Less(i, j int) bool {
if arr[i].score == arr[j].score {
return arr[i].written_date > arr[j].written_date
} else {
return arr[i].score > arr[j].score
}
}

func (arr RankingSlice) Swap(i, j int) {
arr[i], arr[j] = arr[j], arr[i]
}
“`

소팅을 하기위한 준비는 이제 끝났다.
테스트 코드를 만들고 테스트 해보자.

“`
func sortRankings(){
rankings := []Ranking {
{“88154420822432544”, 1436520192, 13960},
{“91670176688136577”, 1440799941, 13080},
{“88180751521592112”, 1435637878, 14280},
{“88144324860977520”, 1437278775, 13560},
{“93797540915256451”, 1435889873, 14200},
{“88249875880704624”, 1438226613, 11960},
{“88192601275706464”, 1436694190, 13320},
{“88148115536237952”, 1436450939, 13600},
{“89047353485705664”, 1435654249, 12760},
{“88182263314045056”, 1436586635, 13320},
{“88193534078563601”, 1438176365, 16600},
{“88256711722920353”, 1437136853, 8920},
{“89852877819675984”, 1436684270, 12680},
{“93594773313085234”, 1437634370, 8480},
{“90289273133727025”, 1436351481, 10800},
{“92150085612088915”, 1440609977, 12720},
{“91211930152797040”, 1438076721, 10920},
{“88309448624594448”, 1438057506, 10440},
{“88406950494717233”, 1436508922, 11000},
{“93176928270204371”, 1437293064, 10440},
{“88056851903088048”, 1436338570, 10440},
{“93691000129391522”, 1436618165, 12560},
{“88675559501659840”, 1438230885, 9800},
}

sort.Sort(RankingSlice(rankings))
fmt.Println(rankings)
}
“`

크게 한것 없이 정렬이 끝났다. 아래와 같은 결과가 나올 것이다.

“`
kakaoid 88193534078563601 score 16600 written_date 1438176365
kakaoid 88180751521592112 score 14280 written_date 1435637878
kakaoid 93797540915256451 score 14200 written_date 1435889873
kakaoid 88154420822432544 score 13960 written_date 1436520192
kakaoid 88148115536237952 score 13600 written_date 1436450939
kakaoid 88144324860977520 score 13560 written_date 1437278775
kakaoid 88192601275706464 score 13320 written_date 1436694190
kakaoid 88182263314045056 score 13320 written_date 1436586635
kakaoid 91670176688136577 score 13080 written_date 1440799941
kakaoid 89047353485705664 score 12760 written_date 1435654249
kakaoid 92150085612088915 score 12720 written_date 1440609977
kakaoid 89852877819675984 score 12680 written_date 1436684270
kakaoid 93691000129391522 score 12560 written_date 1436618165
kakaoid 88249875880704624 score 11960 written_date 1438226613
kakaoid 88406950494717233 score 11000 written_date 1436508922
kakaoid 91211930152797040 score 10920 written_date 1438076721
kakaoid 90289273133727025 score 10800 written_date 1436351481
kakaoid 88309448624594448 score 10440 written_date 1438057506
kakaoid 93176928270204371 score 10440 written_date 1437293064
kakaoid 88056851903088048 score 10440 written_date 1436338570
kakaoid 88675559501659840 score 9800 written_date 1438230885
kakaoid 88256711722920353 score 8920 written_date 1437136853
kakaoid 93594773313085234 score 8480 written_date 1437634370
“`

처음에는 퀵소트를 구현해서 정렬하려고 퀵소트를 만들기 까지 했지만, 너무 잘 만들어놓은 패키지 때문에 큰 노력없이 좋은 결과를 얻을 수 있었다.
위에서 테스트로 만든 것 이외에도 소팅할 키를 지정하기, 멀티키로 소팅하기 같은 기능도 sort패키지를 사용하면 손쉽게(?!) 구현할 수 있다.
관련 정보는 golang.org의 sort 패키지를 확인해 보자.

[sort패키지](https://golang.org/pkg/sort/)

전체 소스코드는 아래의 링크에 있다.

[소스코드보기](https://gist.github.com/wapj/5b75ad16d32727eed61036fdb6c349e7)

[/markdown]