다음과 같이 반려동물에 대한 정보를 담고 있는 중첩된(nested) JavaScript 객체가 있다고 가정해 보겠습니다.
const pets = {
owner1: 'Frank',
owner2: 'Curly',
owner3: 'Maurice',
dogs: {
terriers: {
name1: 'Fido',
name2: 'Woofy',
name3: {
goodDog: 'Frank',
badDog: 'Judas',
}
},
poodles: {
name1: 'Curly',
name2: 'Fido',
},
},
};문제 정의
우리는 이러한 객체를 인자로 받는 JavaScript 함수를 작성해야 합니다. 함수는 객체 전체를 탐색하여 존재하는 모든 중복 값을 찾아낸 뒤, 그 중복 값들을 하나의 배열에 담아 반환해야 합니다.
예를 들어 위 객체를 입력으로 사용한다면, 기대되는 출력 결과는 다음과 같습니다.
const output = ['Frank', 'Curly', 'Fido'];
구현 방법
이 문제는 재귀(recursion)를 활용하면 깔끔하게 해결할 수 있습니다. 객체의 각 키를 순회하면서 값이 또 다른 객체라면 해당 객체에 대해 재귀 호출을 수행하고, 원시 값이라면 맵(map) 객체에 등장 횟수를 기록합니다. 어떤 값이 두 번째로 등장하는 시점에 결과 배열에 추가하면, 값이 몇 번이든 반복되어도 중복 값이 딱 한 번씩만 수집됩니다.
const pets = {
owner1: 'Frank',
owner2: 'Curly',
owner3: 'Maurice',
dogs: {
terriers: {
name1: 'Fido',
name2: 'Woofy',
name3: {
goodDog: 'Frank',
badDog: 'Judas',
}
},
poodles: {
name1: 'Curly',
name2: 'Fido',
},
},
};
const recursiveSearch = (obj, map = {}, res = []) => {
Object.keys(obj).forEach(key => {
if(typeof obj[key] === "object"){
return recursiveSearch(obj[key], map, res);
};
map[obj[key]] = (map[obj[key]] || 0) + 1;
if(map[obj[key]] === 2){
res.push(obj[key]);
}
});
return res;
};
console.log(recursiveSearch(pets));코드 설명
- recursiveSearch 함수는 세 개의 매개변수를 받습니다. 탐색 대상 객체(obj), 각 값의 등장 횟수를 기록하는 맵(map), 그리고 중복 값이 저장될 결과 배열(res)입니다.
- Object.keys()로 객체의 모든 키를 순회하면서, 값이 객체인 경우에는 동일한 map과 res를 공유하며 재귀적으로 탐색을 이어갑니다.
- 값이 원시 타입이라면 map에서 해당 값의 등장 횟수를 1씩 증가시키고, 횟수가 정확히 2가 되는 순간 결과 배열에 push 합니다. 이 조건 덕분에 같은 값이 세 번 이상 등장해도 결과에 여러 번 중복해서 기록되지 않습니다.
실행 결과
코드를 실행하면 콘솔에 다음과 같이 출력됩니다.
[ 'Frank', 'Curly', 'Fido' ]