다음과 같이 배열 안에 여러 개의 하위 배열이 담긴 2차원 배열이 있다고 가정해 보겠습니다.
const arr = [ ['A','F','A','H','F','F'], ['F','A','A','F','F','H'] ];
이번 글에서는 이러한 배열을 입력으로 받아 지정된 규칙에 따라 정렬하는 자바스크립트 함수를 작성해 보겠습니다.
정렬 규칙
함수는 주어진 배열의 모든 하위 배열을 아래 규칙에 따라 내부적으로 정렬해야 합니다.
- 'A' 또는 'F'가 아닌 요소(예: 'H')는 반드시 원래 위치를 그대로 유지해야 합니다.
- 'A'와 'F'끼리는 알파벳순으로, 즉 'A'가 항상 'F'보다 앞에 오도록 정렬해야 합니다.
따라서 위 배열의 최종 출력 결과는 다음과 같은 형태가 되어야 합니다.
const output = [ ['A','A','A','H','A','F'], ['F','F','F','F','F','H'] ];
주의할 점은, 정렬 과정에서 필요하다면 하위 배열에 속한 요소들이 다른 하위 배열로 옮겨갈 수도 있다는 것입니다. 즉, 배열 전체를 하나의 연속된 시퀀스로 보고 'A'와 'F'의 순서를 맞추되, 'H' 같은 다른 문자는 제자리에 고정하는 방식입니다.
예제 코드
const arr = [
['A','F','A','H','F','F'],
['F','A','A','F','F','H']
];
const customSort = (arr = []) => {
// 모든 하위 배열의 요소를 하나의 1차원 배열로 평탄화
const order = [].concat(...arr.slice());
const res = [];
order.forEach((el, ind) => {
// 'A'를 만나면 그보다 앞에 있는 'F'와 위치를 교환
if (el === 'A') {
const fIndex = order.indexOf('F');
if (fIndex < ind) {
order[fIndex] = 'A';
order[ind] = 'F';
}
}
});
// 재배열된 순서를 원래 하위 배열의 길이에 맞게 잘라내어 반환
arr.forEach(el => res.push(order.splice(0, el.length)));
return res;
};
console.log(customSort(arr));
실행 결과
콘솔에는 다음과 같이 출력됩니다.
[ [ 'A', 'A', 'A', 'H', 'A', 'F' ], [ 'F', 'F', 'F', 'F', 'F', 'H' ] ]
동작 원리
이 코드의 핵심 로직은 세 단계로 정리할 수 있습니다.
- 평탄화: [].concat(...arr)을 사용해 2차원 배열의 모든 요소를 하나의 1차원 배열로 합칩니다. 덕분에 하위 배열의 경계와 무관하게 'A'와 'F'의 상대적인 순서를 자유롭게 조정할 수 있습니다.
- 교환: 순회 도중 'A'를 만나면 그보다 앞쪽에 위치한 'F'의 인덱스를 찾아 두 값을 서로 바꿉니다. 이 과정을 반복하면 어떤 'F'도 자신보다 앞선 'A'보다 먼저 등장하지 않게 됩니다.
- 재분할: 정렬이 완료된 배열을 원래 하위 배열의 길이(각 6개)에 맞춰 다시 잘라내어 최종 결과를 구성합니다.
이 방식은 복잡한 정렬 알고리즘을 도입하지 않고도 단순한 값 교환만으로 요구 사항을 충족한다는 점이 특징입니다. 다만 요소 개수가 매우 많아질 경우 indexOf 탐색이 반복되어 성능이 저하될 수 있으므로, 대용량 데이터에는 다른 접근 방식을 고려하는 것이 좋습니다.