JavaScript로 개발을 하다 보면 배열 안에서 특정 숫자와 가장 가까운 값을 찾아야 하는 경우가 자주 발생합니다. 예를 들어 슬라이더 값 보정, 좌표 매칭, 데이터 정규화 등 다양한 상황에서 유용하게 활용할 수 있습니다.
이번 글에서는 배열과 하나의 숫자를 입력받아, 그 숫자와 가장 가까운 요소의 인덱스를 반환하는 함수를 작성하는 방법을 알아보겠습니다.
문제 정의
다음과 같은 배열이 있다고 가정해 보겠습니다.
const arr = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
이 배열과 숫자 n(예: 150)을 받는 JavaScript 함수를 작성해야 합니다. 함수는 배열의 요소 중 n에 가장 가까운 항목의 인덱스를 반환해야 합니다.
구현 코드
가장 기본적이고 직관적인 방법은 배열을 순회하면서 각 요소와 목표 숫자 사이의 절대 차이를 비교하는 것입니다.
const arr = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
const closestIndex = (num, arr) => {
let curr = arr[0], diff = Math.abs(num - curr);
let index = 0;
for (let val = 0; val < arr.length; val++) {
let newdiff = Math.abs(num - arr[val]);
if (newdiff < diff) {
diff = newdiff;
curr = arr[val];
index = val;
}
}
return index;
};
console.log(closestIndex(150, arr));실행 결과
위 코드를 실행하면 콘솔에 다음과 같은 결과가 출력됩니다.
4
숫자 150과 가장 가까운 배열 요소는 인덱스 4에 있는 162입니다. 실제로 |150 − 162| = 12로, 다른 어떤 요소보다도 차이가 작습니다.
코드 동작 원리
이 함수의 로직은 다음과 같이 단계별로 이해할 수 있습니다.
1. 초기값 설정
첫 번째 요소 arr[0]을 현재 가장 가까운 값(curr)으로 설정하고, 목표 숫자와의 차이(diff)를 계산합니다. 인덱스도 0으로 초기화합니다.
2. 배열 순회 및 비교
배열의 모든 요소를 순회하면서 각 요소와 목표 숫자의 절대 차이(Math.abs() 사용)를 계산합니다. 새로 계산된 차이가 기존 차이보다 작으면, 해당 요소를 새로운 최솟값으로 갱신하고 그 인덱스를 저장합니다.
3. 결과 반환
순회가 끝나면 가장 작은 차이를 가진 요소의 인덱스를 반환합니다.
reduce()를 활용한 대안 방법
좀 더 함수형 프로그래밍 스타일을 선호한다면 reduce() 메서드를 활용해 같은 로직을 간결하게 표현할 수 있습니다.
const closestIndex = (num, arr) =>
arr.reduce((best, val, i) =>
Math.abs(val - num) < Math.abs(arr[best] - num) ? i : best, 0);
console.log(closestIndex(150, arr)); // 4이 방식은 누적값으로 지금까지 발견한 가장 가까운 요소의 인덱스를 유지하며, 더 가까운 요소를 만나면 인덱스를 갱신합니다.
주의 사항
- 동률 처리: 두 요소가 목표 숫자와 동일한 거리에 있을 경우, 위 구현들은 먼저 등장하는 요소(더 작은 인덱스)를 반환합니다.
- 빈 배열: 빈 배열을 전달하면 오류가 발생할 수 있으므로, 필요하다면 배열 길이 검사를 추가하는 것이 좋습니다.
- 정렬된 배열: 배열이 오름차순으로 정렬되어 있다면 이진 탐색(Binary Search)을 활용해 O(log n) 시간 복잡도로 더 빠르게 찾을 수 있습니다.
마무리
선형 탐색 방식은 시간 복잡도가 O(n)이지만 구현이 간단하고 정렬 여부와 상관없이 동작하기 때문에 대부분의 실무 상황에서 충분히 효율적입니다. 데이터 크기가 매우 크고 배열이 정렬되어 있는 특수한 경우에만 이진 탐색 최적화를 고려하면 됩니다.