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

JavaScript에서 객체 배열을 재귀로 트리 구조로 변환하는 방법

데이터 처리 작업을 하다 보면 부모-자식 관계가 평면적인(flat) 객체 배열로 표현된 데이터를 계층적인 트리 구조로 변환해야 하는 경우가 자주 발생합니다. 이번 글에서는 다음과 같은 객체 배열이 주어졌을 때 이를 트리 구조의 JSON으로 변환하는 방법을 알아보겠습니다.

const arr = [
    {
        "parentIndex": '0' ,
        "childIndex": '3' ,
        "parent": "ROOT",
        "child": "root3"
    },
    {
        "parentIndex": '3' ,
        "childIndex": '2' ,
        "parent": "root3" ,
        "child": "root2"
    },
    {
        "parentIndex": '3' ,
        "childIndex": '1' ,
        "parent": "root3" ,
        "child": "root1"
    }
];

문제 정의

위와 같은 객체 배열을 입력으로 받아 재귀(recursion)를 활용해 해당 JSON을 트리 구조로 변환하는 JavaScript 함수를 작성해야 합니다.

최종적으로 만들고자 하는 트리 구조는 다음과 같습니다.

nodeStructure: {
    text: { name: "root3" },
    children: [
        {
            text: { name: "root2" }
        },
        {
            text: { name: "root1" }
        }
    ]
};

해결 접근 방식

핵심 아이디어는 다음과 같습니다.

1. 루트 키('ROOT')에서 시작하여 해당 부모를 가진 모든 항목을 찾습니다.
2. 각 자식 항목에 대해 노드 객체를 생성합니다.
3. 해당 자식이 다시 부모 역할을 하는지 확인하기 위해 같은 함수를 재귀적으로 호출합니다.
4. 자식이 존재하면 children 속성에 추가하고, 없으면 생략합니다.

먼저 조건에 맞는 항목만 걸러내는 헬퍼 함수 partial을 정의하고, 이를 활용해 재귀 함수 findNodes를 구현합니다.

예제 코드

전체 구현 코드는 다음과 같습니다.

const arr = [
    {
        "parentIndex": '0' ,
        "childIndex": '3' ,
        "parent": "ROOT",
        "child": "root3"
    },
    {
        "parentIndex": '3' ,
        "childIndex": '2' ,
        "parent": "root3" ,
        "child": "root2"
    },
    {
        "parentIndex": '3' ,
        "childIndex": '1' ,
        "parent": "root3" ,
        "child": "root1"
    }
];
const partial = (arr = [], condition) => {
    const result = [];
    for (let i = 0; i < arr.length; i++) {
        if(condition(arr[i])){
            result.push(arr[i]);
        }
    }
    return result;
}
const findNodes = (parentKey,items) => {
    let subItems = partial(items, n => n.parent === parentKey);
    const result = [];
    for (let i = 0; i < subItems.length; i++) {
        let subItem = subItems[i];
        let resultItem = {
            text: {name:subItem.child}
        };
        let kids = findNodes(subItem.child , items);
        if(kids.length){
            resultItem.children = kids;
        }
        result.push(resultItem);
    }
    return result;
}
console.log(JSON.stringify(findNodes('ROOT', arr), undefined, 4));

실행 결과

콘솔에 출력되는 결과는 다음과 같습니다.

[
    {
        "text": {
            "name": "root3"
        },
        "children": [
            {
                "text": {
                    "name": "root2"
                }
            },
            {
                "text": {
                    "name": "root1"
                }
            }
        ]
    }
]

코드 설명

partial 함수는 배열과 조건 함수를 인자로 받아 조건을 만족하는 요소들만 모아 새로운 배열로 반환하는 필터링 헬퍼입니다. 실무에서는 JavaScript 내장 메서드인 Array.prototype.filter()로 대체할 수 있습니다.

findNodes 함수는 부모 키를 기준으로 일치하는 항목들을 찾은 뒤, 각 항목을 { text: { name } } 형태의 노드로 변환합니다. 그리고 각 노드의 자식 값이 다시 부모 키로 등장하는지 재귀적으로 검사하여, 하위 노드가 존재하는 경우에만 children 속성을 붙입니다.

이러한 재귀적 접근 방식은 깊이에 제한 없이 임의의 계층 구조를 처리할 수 있다는 장점이 있으며, 조직도, 카테고리 메뉴, 파일 시스템 탐색기 등 다양한 트리형 UI 데이터를 구성할 때 널리 활용됩니다.