문제 상황
다음과 같이 여러 단계로 중첩된 숫자 배열이 있다고 가정해 보겠습니다.
const arr = [1, 4, 5, [
5, 6, [
6, 19, 5, [5]
], [5, 7, 6, [6, 8]], 8
], 6];
여기서 우리가 작성해야 할 것은 임의의 깊이까지 중첩된 배열을 입력으로 받아, 그 배열을 완전히 펼친(flat) 새로운 배열을 반환하는 자바스크립트 함수입니다.
피해야 할 제약 조건
함수를 구현할 때 다음 두 가지 조건을 반드시 지켜야 합니다.
코드 어디에서도 직접 작성한 재귀 함수(recursive function)를 사용할 수 없습니다.
배열 평탄화에 흔히 쓰이는 Array.prototype.flat() 메서드를 사용할 수 없습니다.
즉, 순수한 반복문만으로 다차원 배열을 1차원으로 만들어내는 것이 핵심 과제입니다.
해결 코드
이 문제는 while 루프와 레벨(level)·카운터(counter) 추적 기법을 활용하면 해결할 수 있습니다. 각 깊이에서 현재 순회 중인 위치를 기억해 두고, 배열 요소를 만나면 한 단계 더 들어갔다가 끝나면 다시 돌아오는 방식입니다.
const arr = [1, 4, 5, [
5, 6, [
6, 19, 5, [5]
], [5, 7, 6, [6, 8]], 8
], 6];
const flattenWithoutRecursion = (arr = []) => {
const res = [];
let level = 0, ref = [arr], counter = [0];
while (level >= 0) {
// 현재 레벨의 모든 요소를 순회했다면 한 단계 위로 올라감
if (counter[level] >= ref[level].length) {
level--;
continue;
}
// 요소가 배열이라면 한 단계 더 깊이 진입
if (Array.isArray(ref[level][counter[level]])) {
ref[level + 1] = ref[level][counter[level]];
counter[level]++;
level++;
counter[level] = 0;
continue;
}
// 일반 값이라면 결과 배열에 추가
res.push(ref[level][counter[level]]);
counter[level]++;
}
return res;
};
console.log(flattenWithoutRecursion(arr));
동작 원리
이 알고리즘은 사실상 재귀를 명시적 스택 구조로 대체한 것과 같습니다. ref 배열은 각 깊이에서 참조 중인 배열을 저장하고, counter 배열은 해당 깊이에서 읽고 있는 인덱스를 추적합니다.
현재 요소가 배열이면 level을 1 증가시켜 내부로 진입하고, 새 레벨의 카운터를 0으로 초기화합니다.
현재 레벨의 끝에 도달하면 level을 1 감소시켜 상위 배열로 되돌아갑니다.
일반 값(숫자 등)이면 결과 배열 res에 순서대로 push합니다.
level이 0 아래로 내려가면 모든 탐색이 끝난 것이므로 while 루프가 종료됩니다.
덕분에 중첩이 몇 단계든 관계없이, 재귀 호출이나 flat() 메서드 없이도 안정적으로 동작합니다.
실행 결과
콘솔에는 다음과 같이 완전히 평탄화된 1차원 배열이 출력됩니다.
[
1, 4, 5, 5, 6, 6,
19, 5, 5, 5, 7, 6,
6, 8, 8, 6
]