다음과 같이 객체로 이루어진 배열이 있다고 가정해 보겠습니다. 각 객체는 userId, from, to 속성을 가지며, 특정 사용자의 구간 정보를 나타냅니다.
const arr = [
{userId: "3t5bsFB4PJmA3oTnm", from: 1, to: 6},
{userId: "3t5bsFB4PJmA3oTnm", from: 7, to: 15},
{userId: "3t5bsFB4PJmA3oTnm", from: 172, to: 181},
{userId: "3t5bsFB4PJmA3oTnm", from: 182, to: 190}
];여기서 우리가 작성해야 할 것은 이러한 배열을 입력으로 받아, "from"과 "to" 속성을 기준으로 서로 인접하거나 겹치는 객체들을 하나의 객체로 병합하는 JavaScript 함수입니다. 즉, 앞 구간의 끝(to)과 뒤 구간의 시작(from)이 연속되는 경우 이를 하나의 범위로 합쳐야 합니다. 원하는 출력 결과는 다음과 같습니다.
const output = [
{userId: "3t5bsFB4PJmA3oTnm", from: 1, to: 15},
{userId: "3t5bsFB4PJmA3oTnm", from: 172, to: 190}
];구현 예제
배열 메서드 reduce()를 활용하면 깔끔하게 해결할 수 있습니다. 아래 코드를 살펴보겠습니다.
const arr = [
{userId: "3t5bsFB4PJmA3oTnm", from: 1, to: 6},
{userId: "3t5bsFB4PJmA3oTnm", from: 7, to: 15},
{userId: "3t5bsFB4PJmA3oTnm", from: 172, to: 181},
{userId: "3t5bsFB4PJmA3oTnm", from: 182, to: 190}
];
const groupByDuration = (arr = []) => {
const result = arr.reduce((acc, val) => {
let last = acc[acc.length - 1] || {};
if (last.userId === val.userId && last.to + 1 === val.from) {
last.to = val.to;
} else {
acc.push({ userId: val.userId, from: val.from, to: val.to });
}
return acc;
}, []);
return result;
}
console.log(groupByDuration(arr));코드 동작 원리
이 함수의 핵심 로직은 다음과 같이 정리할 수 있습니다.
- 누적 배열 초기화:
reduce()의 초기값으로 빈 배열[]을 지정하여, 병합된 결과를 담을acc배열을 만듭니다. - 마지막 요소 확인:
acc[acc.length - 1]로 누적 배열의 마지막 객체를 가져오고, 배열이 비어 있으면 빈 객체{}를 반환합니다. - 연속 여부 판단: 마지막 객체와 현재 객체의
userId가 같고,last.to + 1 === val.from조건을 만족하면 두 구간이 연속된 것으로 판단합니다. - 병합 또는 추가: 연속된 경우에는 마지막 객체의
to값만 현재 값으로 갱신하고, 그렇지 않으면 현재 객체를 새로운 항목으로 추가합니다.
출력 결과
코드를 실행하면 콘솔에 다음과 같은 결과가 출력됩니다.
[
{ userId: '3t5bsFB4PJmA3oTnm', from: 1, to: 15 },
{ userId: '3t5bsFB4PJmA3oTnm', from: 172, to: 190 }
]첫 번째와 두 번째 객체는 구간이 연속되므로 from: 1, to: 15로 병합되었고, 세 번째와 네 번째 객체 역시 from: 172, to: 190으로 합쳐진 것을 확인할 수 있습니다. 이처럼 reduce()를 활용하면 정렬된 구간 데이터를 효율적으로 그룹화할 수 있습니다.