실제 개발을 하다 보면 객체 안에 또 다른 객체가 속성 값으로 들어 있는 경우가 많습니다. 이런 중첩 구조는 2~3단계는 물론 그 이상 깊어지기도 합니다.
예시 데이터
다음은 사람 정보를 담고 있는 샘플 객체입니다. 각 사람의 이름 아래에 fullName과 details 객체가 중첩되어 있습니다.
const people = {
Ram: {
fullName: 'Ram Kumar',
details: {
age: 31,
isEmployed: true
}
},
Sourav: {
fullName: 'Sourav Singh',
details: {
age: 22,
isEmployed: false
}
},
Jay: {
fullName: 'Jay Grewal',
details: {
age: 26,
isEmployed: true
}
}
}문제 정의
우리가 만들어야 할 함수는 다음과 같은 역할을 수행해야 합니다.
- 객체와 검색할 문자열(키 이름)을 인자로 받습니다.
- 객체 전체를 탐색하면서 해당 문자열과 일치하는 키를 찾습니다.
- 일치하는 모든 키의 값을 배열에 담아 반환합니다.
이 함수를 recursiveSearch()라고 부르겠습니다. 객체가 여러 단계로 중첩되어 있기 때문에 재귀(recursion) 방식이 가장 적합한 접근법입니다.
구현 코드
const people = {
Ram: {
fullName: 'Ram Kumar',
details: {
age: 31,
isEmployed: true
}
},
Sourav: {
fullName: 'Sourav Singh',
details: {
age: 22,
isEmployed: false
}
},
Jay: {
fullName: 'Jay Grewal',
details: {
age: 26,
isEmployed: true
}
}
}
const recursiveSearch = (obj, searchKey, results = []) => {
const r = results;
Object.keys(obj).forEach(key => {
const value = obj[key];
if(key === searchKey && typeof value !== 'object'){
r.push(value);
}else if(typeof value === 'object'){
recursiveSearch(value, searchKey, r);
}
});
return r;
};
console.log(recursiveSearch(people, 'age'));실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ 31, 22, 26 ]
코드 동작 원리
이 함수의 동작 과정을 단계별로 살펴보면 다음과 같습니다.
- 먼저 메인 객체의 모든 키를 순회합니다.
- 순회 중 만난 값이 객체라면, 해당 하위 객체에 대해 자기 자신(
recursiveSearch)을 다시 호출하여 재귀적으로 탐색을 이어갑니다. - 현재 키가 찾고자 하는 키와 일치하고, 그 값이 객체가 아니라면 즉시 결과 배열(
results)에 값을 저장합니다. - 모든 순회가 끝나면 원하는 값들이 담긴 결과 배열을 반환합니다.
여기서 한 가지 주목할 점은 세 번째 매개변수 results = []입니다. 기본값으로 빈 배열을 사용하기 때문에 최초 호출 시에는 별도의 배열을 넘겨줄 필요가 없고, 재귀 호출 시에는 동일한 배열을 계속 전달하여 값을 누적할 수 있습니다.
시간 복잡도
이 함수의 시간 복잡도는 O(m × n)입니다. 여기서 n은 메인 객체 내부에 있는 하위 객체의 수, m은 가장 깊은 중첩 단계를 의미합니다.