Làm phẳng (flatten) array trong JS sử dụng Array.flat()

Đã bao giờ bạn nghe đến từ làm phẳng mảng trong JS chưa (hay bất kể ngôn ngữ nào khác cũng được), trong thuật ngữ tiếng anh nó được gọi là flatten array. Flatten array/mảng là khi bạn đưa tất cả phần tử trong một mảng về cùng cấp độ với nhau, ví dụ mảng A chứa số 1 và chứa một mảng con B, trong mảng con B chứa số 2, làm phẳng sẽ đưa số 2 lên đứng ngang cấp với số 1, không còn mảng lồng nhau trong mảng A nữa. Lý thuyết khó hiểu của mình đấy. Ví dụ thực tế nè. À đúng rồi, xuyên suốt bài mình sẽ dùng từ flatten thay cho làm phẳng nha.

1
2
3
4
5
6
var nested = [ 1, 2, [ 3, 4 ], [ 5 ] ];

var flattened = nested.flat();

console.log(flattened);
// -> kết quả là: [ 1, 2 , 3, 4, 5]

Vậy nếu có 3 mảng lồng nhau thì thế nào nhỉ?

Thêm tham số độ sâu muốn flatten

Cú pháp: array.flat(<độ sâu>)

Ở ví dụ trên độ sâu (deep) của mảng là 1 bởi vì mảng nested chứa bên trong nó mảng con mà mảng con này không chứa mảng con khác (kiểu giống gia phả, hoặc đa cấp á, tới 1 cấp là hết rồi á). Lúc này nếu chỉ gọi flat() không thôi, mặc định nó sẽ nhận độ sâu là 1 chỉ làm phẳng 1 cấp độ.

Ví dụ:

1
2
3
4
5
6
7
var nested = [ 1, 2, [ 3, 4 ], [ 5, [ 6, 7 ] ] ];

var flattened = nested.flat(); // hoặc nested.flat(1); cũng giống nhau

console.log(flattened);
// -> kết quả là: [1, 2, 3, 4, 5, Array(2)]
// [1, 2, 3, 4, 5, [6, 7]]

Để flatten tất cả phần tử, ta viết lại:

1
2
3
4
5
6
var nested = [ 1, 2, [ 3, 4 ], [ 5, [ 6, 7 ] ] ];

var flattened = nested.flat(2);

console.log(flattened);
// -> kết quả là: [1, 2, 3, 4, 5, 6, 7]

Vậy nhở tớ không biết mảng đó có độ sâu bao nhiêu thì sao, chả nhẽ ngồi đếm ư, huhu, bao giờ mới đi dự hội được :v.

Flatten không cần biết sâu bao nhiêu

Khi không biết sâu bao nhiêu ta thường bảo nhau, nó sâu vô tận, trong JS có một cái tên chỉ điều đó, đó chính là Infinity. Và đúng rồi như bạn nghĩ, chỉ cần pass Infinity vào function flat thôi là tất cả xong xuôi.

1
2
3
4
5
const soDeep = [ [ 1, 2, [ 3, [ 4, [ 5, [ 6, [ 7 ] ] ] ] ], 8 ] ];

soDeep.flat(Infinity);

// [1, 2, 3, 4, 5, 6, 7, 8]

Ồ hóa ra nó gê vậy cơ à, xưa giờ đi làm phẳng toàn xài đệ quy với viết loạn xạ cả lên, nên khỏe nhờ.

Thực ra nó đây là tính năng mới trong ES10, và tất nhiên IE thì khỏi phải nói không hỗ trợ IE nhé.

Xài flat với mảng có phần tử trống

Flat hữu dụng trong khá nhiều trường hợp và đây là một ví dụ:

1
2
3
4
var missingNumbers = [1, , 3, , 5];

missingNumbers.flat();
// [1, 3, 5];

Nó sẽ tự động loại bỏ empty slot (phần tử trống) trong array.

Giải pháp thay thế

ES6

Cách này chỉ có thể flatten ở 1 level thôi nha.

1
2
3
4
var oneLevelDeep = [ [ 1, 2 ], [ 3 ] ];

var flattened = [].concat(...oneLevelDeep);
// [1, 2, 3,]

Cũ, cũ hơn cả ES6

Cũng tương tự như trên, chỉ có thể flatten ở 1 level thôi.

1
2
3
4
var oneLevelDeep = [ [1, 2], [3]];

var flattened = [].concat.apply([], oneLevelDeep);
// [1, 2, 3,]

Dùng reduceconcat (thực ra là đệ quy)

Đây là giải pháp đến từ MDN web doc các bạn tham khảo nhé.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var arr1 = [ 1, 2, 3, [ 1, 2, 3, 4, [ 2, 3, 4 ] ] ];

function flattenDeep(arr1) {
var flattened = arr1.reduce(function (acc, val) {
// nếu trong quá trình lặp, phần tử là mảng thì đệ quy nó ngay.
if (Array.isArray(val)) {
acc.concat(flattenDeep(val));
} else {
acc.concat(val);
}
}, []);

// viết ngắn gọn
// const flattened = arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);

return flattened;
}

flattenDeep(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

Bài tập thực hành

Các bạn thử tìm hiểu vào giải bài nào xem sao nhé. Đề bài viết hàm tìm số 213 trong mảng.

1
2
3
4
5
6
7
const haystack =[1, 4, [5,6,7, [8, 18, [34,177,[98,[210,[213]]]]]]];

const needle = 213;

function search(needle, haystack) {
// ...
}

Tham khảo

 Comments
Comment plugin failed to load
Loading comment plugin