[php] usort 사용법과 예시 (ft. 클로저 함수)
php 에서는 array를 정렬하는 여러 가지 함수가 있다.
asort, rsort, ksort, krsort 등등이 있다.
key 값을 기준으로 오름차순으로 정렬,
value 값을 기준으로 오름차순으로 정렬,
또는 내림차순으로 정렬해 주는 기능이다.
근데 php가 기본적으로 제공해 주는 정렬 함수들 중에
내가 원하는 기준이 없다면 어떻게 해야 할까?
정렬하는 기준을 내가 직접 지정해 줄 수도 있다.
바로 usort 라는 함수를 이용하면 가능하다.
usort($arr, "ucmp");
$arr라는 배열을 가져와서 정렬하고,
정렬하는 기준은 "ucmp"라는 함수라는 뜻이다.
그러려면 먼자 ucmp라는 함수를 만들어 놓아야 한다.
ucmp에 대해서 조금 더 자세히 알아보자.
ucmp라는 이름의 함수를 찾아서 ucmp()를 실행한다.
이때, 배열 안에 들어 있는 녀석들 2개를 꺼내서 파라미터로 넘겨준다.
즉, $arr로부터 $a와 $b를 꺼내 와서 ucmp($a, $b); 를 실행한다.
그럼 파라미터로 넘겨 받은 이 두 녀석을 ucmp() 함수 안에서 비교한 뒤에
어느 놈을 앞에 두고 어느 놈을 뒤에 둘지 결정해 준다.
그 결정을 기준 삼아서 앞에서부터 뒤까지 정렬을 완성한다.
ucmp() 함수에서는 누구를 앞에 두고 누구를 뒤에 둘지 결정해 줘야 한다고 했다.
어떻게 결정해 준다는 말일까?
ucmp() 함수를 실행할 때 파라미터 2개를 넘겨 준다.
예를 들어, $a와 $b를 던져준다고 하자.
이 둘은 순서를 지켜야 하므로 주의해야 한다.
ucmp($a, $b); 이렇게 넘겨준다고 하면,
$a == $b 일 때는 0을 리턴하고,
$a가 앞일 때는 양수를 리턴하고,
$b가 앞일 때는 음수를 리턴한다.
아래는 기본 sort();를 굳이 usort(); 로 구현해본 것.
안이 어떻게 돼 있는 건가 뜯어본다는 느낌으로 참고.
function cmp($a, $b)
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array(3, 2, 5, 6, 1);
usort($a, "cmp");
아래는 연관배열을 어떤 특정한 key 값을 기준으로 정렬해야 할 때.
function cmp($a, $b)
{
return strcmp($a["color"], $b["color"]);
}
$fruits[] = array("fruit" => "lemons", "color" => "yellow");
$fruits[] = array("fruit" => "apples", "color" => "red");
$fruits[] = array("fruit" => "grapes", "color" => "green");
usort($fruits, "cmp");
//[0] => [fruit] => grapes [color] => green
//[1] => [fruit] => apples [color] => red
//[2] => [fruit] => lemons [color] => yellow
자, 여기서 조금 더 깊이 나아가 보자.
(아래 내용은 구조가 조금 복잡하기 때문에 지금까지 이해한 내용이 더 헷갈릴 수 있다.
위 내용까지만 이해하고 잘 활용해도 충분하므로 여기서 뒤로가기를 눌러도 좋다.)
위에서 살펴본 대로, 연관배열을 정렬할 때는
key 값이든 val 값이든, 뭔가를 기준으로 정렬해야 한다.
그런데 어떤 key값을 기준으로 정렬할지가 상황에 따라 다르다면?
그럼 어떤 키로 정렬할 것인지까지도 파라미터로 받아야 한다.
usort에서 자체적으로 파라미터를 2개 넘겨주는 것도 있고,
거기에 내가 넣어주고 싶은 파라미터도 따로 받아야 한다.
이럴 땐 어떻게 해야 할까?
'클로저 함수'라는 방식를 이용하면 된다.
찬찬히 살펴볼 테니 차근차근 따라가 보자.
일단 usort($arr, "func_name"); 해서
usort를 쓰려면 두 번째 인자로 함수를 넘겨줘야 한다.
여기까지는 고정이다.
그런데 그 자리에 들어가는 함수가 꼭 정렬 기준을 정해주는 함수라고까지는 안 했다.
그럼 이렇게 하면 어떨까.
일단 어떤 함수를 임시로 그 자리에 넣어 두고,
대신 그 함수는 리턴값으로 다른 함수 하나를 던져준다.
결과적으로는 리턴값으로 뱉은 함수가 그 자리를 대신 채우고 있게 한다.
리턴값으로 나온 함수가 정렬 기준을 정해주는 함수이기만 하면 되기 때문이다.
예를 들어 보자.
원래 그 자리에 있어야 하는 함수는 original_func()라고 해보자.
원래대로라면 usort($arr, "original_func"); 이렇게 있어야 한다.
그 자리에 대신 임시로 다른 함수 넣어두겠다고 해 놓고,
그 함수를 replace_func()라고 해보자.
usort($arr, replace_func()); 이렇게 될 것이다.
이 상태에서 replace_func() 의 리턴값으로 original_func()를 뱉으면 되는 것 아닌가?
결과적으로는 usort($arr, original_func()); 상태가 된다.
자리만 잠시 빌리는 꼴이다.
function replace_func() {
return original_func()
}
usort($arr, replace_func());
usort($arr, original_func());
그리고 임시로 넣어 둔 함수 replace_func()를 부를 때는
어떤 $key를 사용해서 배열을 정렬할지 그 값을 파라미터로 던져주자.
usort($array, replace_func($key));
그럼 replace_func는 그 key값을 받아서 original_func를 조작하면 된다.
function replace_func($key) {
return original_func($a, $b) use ($key){
if($a[$key] == $b[$key]) return 0;
return $a[$key] > $b[$key] ? 1 : -1;
}
}
클로저 함수와 usort 함수를 조합한 방식이다.