문제 상황
다음과 같이 동일한 구조의 하위 배열이 여러 번 반복되어 포함된 2차원 배열이 있다고 가정해 보겠습니다.
const arr = [
[
"Serta",
"Black Friday"
],
[
"Serta",
"Black Friday"
],
[
"Simmons",
"Black Friday"
],
[
"Simmons",
"Black Friday"
],
[
"Simmons",
"Black Friday"
],
[
"Simmons",
"Black Friday"
]
];여기서 요구되는 것은 이러한 배열을 입력으로 받아, 원본 배열에 존재하는 고유한(unique) 하위 배열만 담고 있는 새로운 배열을 반환하는 JavaScript 함수를 작성하는 것입니다.
해결 방법: JSON.stringify와 filter 활용
배열은 참조 타입이기 때문에 === 연산자나 indexOf로는 값이 같아도 서로 다른 객체로 판단됩니다. 따라서 각 하위 배열을 JSON.stringify()로 문자열로 직렬화한 뒤, 이 문자열을 키로 사용해 이미 등장했는지 여부를 추적하면 손쉽게 중복을 걸러낼 수 있습니다.
const arr = [
["Serta", "Black Friday"],
["Serta", "Black Friday"],
["Simmons", "Black Friday"],
["Simmons", "Black Friday"],
["Simmons", "Black Friday"],
["Simmons", "Black Friday"]
];
const filterCommon = arr => {
const map = Object.create(null);
const res = arr.filter(el => {
const str = JSON.stringify(el);
const bool = !map[str]; // 처음 등장한 배열인지 확인
map[str] = true;
return bool;
});
return res;
};
console.log(filterCommon(arr));실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ [ 'Serta', 'Black Friday' ], [ 'Simmons', 'Black Friday' ] ]
동작 원리 정리
Object.create(null)로 프로토타입이 없는 순수한 맵 객체를 생성하여 키 충돌 위험을 없앱니다.filter()가 각 하위 배열을 순회할 때마다 해당 배열을 문자열로 변환합니다.- 맵에 아직 존재하지 않는 키라면 처음 등장한 고유한 배열이므로 결과에 포함됩니다.
- 이미 존재하는 키라면 중복이므로
false를 반환해 제외됩니다.
참고: Set을 활용한 대안
ES6의 Set을 사용하면 같은 로직을 더 간결하게 표현할 수도 있습니다.
const filterCommon = arr => {
const seen = new Set();
return arr.filter(el => {
const str = JSON.stringify(el);
if (seen.has(str)) return false;
seen.add(str);
return true;
});
};두 방식 모두 시간 복잡도는 O(n)으로 효율적이며, 하위 배열의 요소 순서까지 완전히 동일한 경우에만 중복으로 처리한다는 점을 유의하시기 바랍니다.