Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript 재귀 함수로 중첩 객체에서 특정 키의 값 찾기

실무에서는 프로퍼티 값으로 또 다른 객체를 갖는 객체를 자주 다루게 됩니다. 이런 객체는 2~3단계, 혹은 그 이상으로 깊게 중첩되어 있을 수 있습니다. 이 글에서는 재귀(recursion)를 활용해 중첩된 객체 전체에서 원하는 키를 검색하고, 일치하는 모든 값을 배열로 반환하는 방법을 알아보겠습니다.

예시 객체

다음과 같이 사람 이름을 키로 하고, 그 안에 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()라고 지정하겠습니다.

recursiveSearch() 전체 코드

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'));

코드 동작 방식

이 함수의 로직을 단계별로 살펴보면 다음과 같습니다.

1. 먼저 Object.keys()로 메인 객체의 모든 키를 순회합니다.
2. 순회 중 만난 값이 객체라면, 해당 하위 객체를 대상으로 자기 자신을 다시 호출하여(재귀) 원하는 키를 계속 검색합니다.
3. 키가 검색 문자열과 일치하고, 그 값이 객체가 아니라면 즉시 그 값을 results 배열에 저장합니다.
4. 모든 순회가 끝나면 원하는 값들이 담긴 results 배열을 최종적으로 반환합니다.

여기서 한 가지 주목할 점은 results 배열을 세 번째 매개변수의 기본값(default parameter)으로 사용했다는 것입니다. 덕분에 재귀 호출 시 같은 배열을 계속 공유하면서 값을 누적할 수 있어, 별도의 외부 변수 없이 깔끔하게 구현됩니다.

시간 복잡도

이 함수의 시간 복잡도는 O(m × n)입니다. 여기서 n은 메인 객체 내부의 자식 객체 수, m은 가장 깊은 중첩 단계를 의미합니다. 객체의 깊이가 깊어질수록 재귀 호출이 그만큼 더 진행되기 때문입니다.

다른 키로 검색하기

같은 함수를 활용하면 어떤 키든 손쉽게 검색할 수 있습니다. 예를 들어 isEmployed 키로 검색하면 다음과 같은 결과를 얻을 수 있습니다.

console.log(recursiveSearch(people, 'isEmployed'));
// [ true, false, true ]

실행 결과

앞서 작성한 코드를 실행하면 콘솔에 다음과 같이 age 키의 값들이 모두 담긴 배열이 출력됩니다.

[ 31, 22, 26 ]