본문으로 바로가기

[ES10] FLAT, FLATMAP

2020. 9. 2. 21:11
반응형

기존에는 중첩된 배열을 펴주기 (flatten) 위해서 여러 가지 방법(reduce, concat, spread operator 등)을 사용하곤 했습니다. 또는 Underscore(Lodash)의 flatten이나 flattendep을 사용했었죠. 그런데 ES10(ES2019)에 들어서 Native JavaScript 자체적으로 flat 기능을 포함하기 시작했습니다. ES10이라고는 하지만 대부분의 브라우저에서도 지원을 하는 것 같습니다.

 

이번 글에서는 ES10에 추가된 두 개의 flat 관련 메소드인 flatflatMap에 대해서 살펴보도록 하겠습니다.

 

 

1. Array.prototype.flat()

  • flat() 메소드는 중첩된 구조의 배열 요소를 지정한 깊이까지 평탄화한 새로운 배열을 생성합니다.
  • depth를 지정할 수 있으며 무제한으로 평탄화 (Infinity 사용) 할 수 있습니다.
  • 원본 배열은 변경하지 않습니다.

[문법]

arr.flat([depth])

 

Parameter Description
depth (옵션) 중첩된 배열 구조를 평탄화할 때 사용할 깊이 값 (default: 1)

 

[예제]

Flat 메소드는 기능상 명확한 동작을 수행하여 특별히 복잡한 요소는 없습니다.

아래 예제를 볼까요?

// 중첩 배열
var arr = [1, 2, 3, [4, 5]];
console.log(arr.flat()); // [1, 2, 3, 4, 5]

 

결과가 명확하죠? 중첩 배열의 경우 arr.flat()을 이용하여 depth가 1인 배열이 생성되었습니다.

하나 더 중첩시켜 볼까요?

// 2차 중첩
var arr = [1, 2, [3, [4, 5]]];
console.log(arr.flat()); // [1, 2, 3, [4, 5]]
console.log(arr.flat(2)); // [1, 2, 3, 4, 5]

 

배열이 2차 중첩된 경우 flat()의 기본 depth는 1이므로, 위의 처음 결과에서 [4, 5]는 flatten 없이 그대로 남아있습니다. 만약 배열 내의 모든 요소를 1차원으로 풀어주려면, 간단하게 flat의 depth option을 2로 설정해주면 바로 해결할 수 있습니다.

 

만약, 입력되는 array의 정확한 깊이를 모르는 상태에서 모든 요소가 평탄화된 배열을 얻고 싶으면 어떻게 해야 할까요?

정답은...

depth를 Infinity라고 설정하면 됩니다. 

var arr = [1, 2, [3, [4, 5]]];
console.log(arr.flat(Infinity)); // [1, 2, 3, 4, 5]


var arr = [1, [[[2]]], [3, [4, [[[[[5]]]]]]]];
console.log(arr.flat(Infinity)); // [1, 2, 3, 4, 5]

 

 

* 참고로 배열 중간에 값이 비어있는 경우, flat 메소드를 이용하면  해당 요소가 제거됩니다.

var arr = [1, 2, ,4 ,5];
console.log(arr); // [1, 2, empty, 4, 5]
console.log(arr.flat()); // [1, 2, 4, 5]

 

Flat 메소드는 기존에는 reduce와 concat 등을 이용해서 구현했었습니다. 참고로만 하시기 바랍니다.

 

✅ 1차 flatten

var arr = [[1, 2, 3], [4], [5, 6], [7, 8, 9]];
var flatArr = arr.reduce((total, value) => total.concat(value));
console.log(flatArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

 

✅ n차 flatten

var arr = [1, 2, [3, 4, [5, 6]], 7];
function flatDeep(arr, d = 1) {
   return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
                : arr.slice();
};

console.log(flatDeep(arr, Infinity)); // [1, 2, 3, 4, 5, 6, 7]

 

 

2. Array.prototype.flatMap()

  • flatMap 메소드는 배열의 각 요소에 대해 map을 수행한 후, 결과를 새로운 배열로 평탄화 합니다.
  • 평탄화 depth는 1로 설정됩니다.

[문법]

arr.flatMap(function(currentValue, index, array), thisValue))

 

Parameter Description
function

(필수) 배열의 각 값에 대해 실행할 함수 (총 3개 인자)

   - currentValue (필수) 배열내에서 순차적으로 입력되는 엘리먼트
   - index (옵션) 현재 엘리먼트의 배열 내 index
   - array (옵션) 현재 엘리먼트가 속한 배열
thisValue (옵션) 함수 내부에서 사용될 this에 대한 값

 

[예제]

FlatMap 메소드는 기능상으로 map()과 flat()이 연계된 내용입니다.

이해를 돕기 위해서 간단한 예제를 들어보겠습니다. 어떤 배열이 있는데 이 배열의 각 요소를 2번씩 반복하는 새로운 배열을 생성한다고 가정해 보겠습니다.

var arr = [1, 2, 3];
var arrDup = arr.map((item)=> [item, item]);

arrDup = arrDup.flat();
console.log(arrDup); // [1, 1, 2, 2, 3, 3]

 

위에서 map과 flat을 이용하여 요소를 2번 반복한 새로운 배열이 생성되었죠?

이 과정을 flatMap을 이용하면 다음과 같이 한 번에 수행할 수 있습니다.

var arr = [1, 2, 3];
var arrDup = arr.flatMap((item)=> [item, item]);
console.log(arrDup); // [1, 1, 2, 2, 3, 3]

 

위의 내용만 보면, 굳이 flatMap을 꼭 써야 할 필요는 없어 보이네요. 그러면 flatMap이 좀 더 유용할 사항에 대한 예제를 좀 더 살펴보겠습니다.

 

[응용 예제]

✅ 1+1 이벤트!

빵을 사서 담고 있는데 이벤트에 속하는 빵을 산 경우는 무료로 하나 더 받는 카트를 구현합니다. 솔직히 약간은 억지성으로 껴 놓은 예제입니다만... 참고용으로 보세요~

var cart = [
    {product:"이벤트빵", onEvent: true, price: 100}, 
    {product:"그냥빵", onEvent: false, price: 1000}];
    
var cartEvent = cart.flatMap((item) => {
	if (item.onEvent) {
    	let freeItem = {...item, price:0};
    	return [item, freeItem]
	} else {
    	return item;
	}
});

console.log(cartEvent); 
// [ {product: "이벤트빵", onEvent: true, price: 100}
//   {product: "이벤트빵", onEvent: true, price: 0}
//   {product: "그냥빵", onEvent: false, price: 1000} 
// ]

 

맺음말

ES10(ES 2019)이라서 뭔가 부담스러울 것 같지만, 사실 Internet Explorer를 제외하면 거의 대부분의 최신 브라우저들은 모두 지원합니다. IE11 지원도 곧 종료될 예정이니 조만간 마음껏 사용해도 되는 시기가 오지 않을까요?

JavaScript Native의 flatten 기능을 사용하면 편리하기는 합니다. 그런데 가끔씩은 학습을 위해서라면 (그리고 코딩 실력을 위해서!) 다른 방법으로 구현된 모양을 공부해보는 것도 나쁘지 않을 것 같습니다.

 

반응형