flatMap
cực kỳ hữu ích trên các bộ sưu tập, nhưng javascript không cung cấp một trong khi có Array.prototype.map
. Tại sao?
Có cách nào để mô phỏng flatMap
trong javascript một cách dễ dàng và hiệu quả khi xác định flatMap
thủ công không?
flatMap
cực kỳ hữu ích trên các bộ sưu tập, nhưng javascript không cung cấp một trong khi có Array.prototype.map
. Tại sao?
Có cách nào để mô phỏng flatMap
trong javascript một cách dễ dàng và hiệu quả khi xác định flatMap
thủ công không?
arr.map(...).reduce(...)
).
Câu trả lời:
Cập nhật: Array.prototype.flatMap
đang trên đường đến ECMAScript gốc. Nó hiện đang ở Giai đoạn 4 .
Nó được hỗ trợ rộng rãi trong nhiều môi trường. Xem liệu nó có hoạt động trong trình duyệt của bạn hay không bằng cách sử dụng đoạn mã dưới đây -
const data =
[ 1, 2, 3, 4 ]
console.log(data.flatMap(x => Array(x).fill(x)))
// [ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 ]
Tại sao không có Array.prototype.flatMap trong javascript?
Bởi vì lập trình không phải là phép thuật và mọi ngôn ngữ không có các tính năng / nguyên thủy mà mọi ngôn ngữ khác có
Vấn đề là
JavaScript cung cấp cho bạn khả năng tự định nghĩa nó
const concat = (x,y) =>
x.concat(y)
const flatMap = (f,xs) =>
xs.map(f).reduce(concat, [])
const xs = [1,2,3]
console.log(flatMap(x => [x-1, x, x+1], xs))
Hoặc viết lại thu gọn hai vòng lặp thành một
const flatMap = (f,xs) =>
xs.reduce((acc,x) =>
acc.concat(f(x)), [])
const xs = [1,2,3]
console.log(flatMap(x => [x-1, x, x+1], xs))
Nếu bạn muốn nó trên Array.prototype
, không có gì ngăn cản bạn
const concat = (x, y) => x.concat(y)
const flatMap = (f, xs) => xs.map(f).reduce(concat, [])
if (Array.prototype.flatMap === undefined) {
Array.prototype.flatMap = function(f) {
return flatMap(f, this)
}
}
const xs = [1, 2, 3]
console.log(xs.flatMap(x => [x-1, x, x+1]))
.as-console-wrapper { top: 0; max-height: 100% !important; }
Array.prototype._mylib_flatMap
), hoặc thậm chí tốt hơn là sử dụng các ký hiệu ES6, thay vì chỉ định nghĩa đơn giản Array.prototype.flatMap
.
map
được định nghĩa là hàm toàn cục! Thứ hai là thực hành không tốt
append
, map
, filter
, foldl
, foldr
trong vô số những người khác trong không gian tên mặc định. Nó không làm cho nó trở thành một thực tiễn xấu vì bạn đã nghe ai đó nói "tiếng cầu là tệ" hoặc "tiếng bản địa mở rộng là xấu" - vợt là một trong những ngôn ngữ được thiết kế tốt nhất. Bạn chỉ không biết làm thế nào / khi nào để làm điều đó một cách thích hợp.
flatMap
đã được TC39 phê duyệt như một phần của ES2019 (ES10). Bạn có thể sử dụng nó như thế này:
[1, 3].flatMap(x => [x, x + 1]) // > [1, 2, 3, 4]
Đây là cách triển khai phương pháp của riêng tôi:
const flatMap = (f, arr) => arr.reduce((x, y) => [...x, ...f(y)], [])
flapMap
gì vậy? Tôi muốn làm phẳng bản đồ của mình, không phải đập nó!
const flatMap = (arr, ...args) => [].concat(...arr.map(...args));
(hoặc sử dụng apply
nếu bạn không thể sử dụng toán tử lây lan) - mặc dù phiên bản concat này có vấn đề về kích thước gọi stack cho mảng lớn như đã đề cập trong một dung dịch thấp hơn
Tôi biết bạn đã nói rằng bạn không muốn tự mình định nghĩa nó, nhưng việc triển khai này là một định nghĩa khá tầm thường.
Cũng có điều này từ cùng một trang github:
Đây là một cách ngắn hơn một chút bằng cách sử dụng es6 spread, tương tự như renaudtertrais's - nhưng sử dụng es6 và không thêm vào nguyên mẫu.
var flatMap = (a, cb) => [].concat(...a.map(cb))
const s = (v) => v.split(',')
const arr = ['cat,dog', 'fish,bird']
flatMap(arr, s)
Một trong hai sẽ giúp đỡ?
Cần lưu ý (nhờ @ftor) rằng "giải pháp" sau này bị "Vượt quá kích thước ngăn xếp lệnh gọi tối đa" nếu được gọi trên một mảng thực sự lớn (ví dụ: 300k phần tử) a
.
[].concat(...(new Array(300000).fill("foo").map(x => x.toUpperCase())))
. Chắc chắn, đó là một trường hợp góc. Nhưng ít nhất bạn nên đề cập đến nó.
Lodash cung cấp một chức năng bản đồ phẳng, đối với tôi thực tế tương đương với Javascript cung cấp nó nguyên bản. Nếu bạn không phải là người dùng Lodash, thì Array.reduce()
phương pháp của ES6 có thể cho bạn kết quả tương tự, nhưng bạn phải ánh xạ-sau đó làm phẳng theo các bước rời rạc.
Dưới đây là ví dụ về từng phương pháp, ánh xạ danh sách các số nguyên và chỉ trả về tỷ lệ cược.
Lodash:
_.flatMap([1,2,3,4,5], i => i%2 !== 0 ? [i] : [])
ES6 Giảm:
[1,2,3,4,5].map(i => i%2 !== 0 ? [i] : []).reduce( (a,b) => a.concat(b), [] )
Một cách tiếp cận khá ngắn gọn là sử dụng Array#concat.apply
:
const flatMap = (arr, f) => [].concat.apply([], arr.map(f))
console.log(flatMap([1, 2, 3], el => [el, el * el]));
Tôi đã làm những thứ như thế này:
Array.prototype.flatMap = function(selector){
return this.reduce((prev, next) =>
(/*first*/ selector(prev) || /*all after first*/ prev).concat(selector(next)))
}
[[1,2,3],[4,5,6],[7,8,9]].flatMap(i => i); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
[{subarr:[1,2,3]},{subarr:[4,5,6]},{subarr:[7,8,9]}].flatMap(i => i.subarr); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
Bây giờ chúng ta có một flatMap()
trong Javascript! Và nó được hỗ trợ khá tốt
Đầu tiên, phương thức flatMap () ánh xạ từng phần tử bằng cách sử dụng một hàm ánh xạ, sau đó làm phẳng kết quả thành một mảng mới. Nó giống với một bản đồ () theo sau là một phẳng () có độ sâu 1
const dublicate = x => [x, x];
console.log([1, 2, 3].flatMap(dublicate))
{ "message": "Uncaught TypeError: [1,2,3].flatMap is not a function", "filename": "https://stacksnippets.net/js", "lineno": 15, "colno": 23 }
Array.prototype.flatMap()
hiện đã có trong JS. Tuy nhiên, không phải tất cả các trình duyệt đều có thể hỗ trợ chúng, hãy kiểm tra tính tương thích của trình duyệt hiện tại trong tài liệu web Mozilla .
Những gì flatMap()
phương thức làm là đầu tiên ánh xạ từng phần tử bằng cách sử dụng một hàm gọi lại làm đối số, sau đó làm phẳng kết quả thành một mảng mới (mảng 2d bây giờ là 1d vì các phần tử được làm phẳng).
Đây cũng là một ví dụ về cách sử dụng hàm:
let arr = [[2], [4], [6], [8]]
let newArr = arr.flatMap(x => [x * 2]);
console.log(newArr);
arr.reduce((arr, v) => (arr.push(...v), arr), [])
nào?