Tại sao không có Array.prototype.flatMap trong javascript?


81

flatMapcự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 flatMaptrong javascript một cách dễ dàng và hiệu quả khi xác định flatMapthủ công không?


10
"Tại sao?" Vì chưa có ai đưa ra đề xuất? Đây là cách đề xuất một tính năng mới . "Có cách nào để mô phỏng flatMap ... định nghĩa flatMap theo cách thủ công không?" Ờ? Tôi không hiểu. Ý bạn là như thế arr.reduce((arr, v) => (arr.push(...v), arr), [])nào?
Felix Kling

(^ đó chỉ là phần làm phẳng, vì vậy tôi đoán nó phải như vậy arr.map(...).reduce(...)).
Felix Kling

Bạn chỉ có thể làm phẳng mảng sau khi .mapping nó.
kennytm

1
Hmm, bạn muốn "mô phỏng" nó nhưng không "định nghĩa" nó. Điều đó có nghĩa là gì?

8
Nó sẽ sớm trở thành một phần của JS. tc39.github.io/proposal-flatMap
Vijaiendran

Câu trả lời:


95

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; }


1
Sẽ tốt hơn nếu không gian tên bất kỳ bổ sung nào vào nguyên mẫu (ví dụ 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.
Fengyang Wang

11
@FengyangWang "được coi là tốt hơn" mang tính chủ quan cao. Nếu đây là ứng dụng của riêng bạn và bạn có lý do để mở rộng các nguyên mẫu của mình, thì không có gì sai với nó. Nếu đó là một thư viện / mô-đun / khuôn khổ mà bạn đang phân phối để người khác sử dụng, thì tôi đồng ý với bạn. Tuy nhiên, các bình luận không phải là nơi để thảo luận về vấn đề này - vấn đề đáng được giải thích và chi tiết hơn so với bình luận.
Cảm ơn bạn

Đầu tiên là xấu xí: flatMap được định nghĩa là hàm sẽ làm cho mã trở nên lộn xộn - hãy tưởng tượng mã chức năng mapđược định nghĩa là hàm toàn cục! Thứ hai là thực hành không tốt
Sergey Alaev

3
@SergeyAlaev re: "hãy tưởng tượng bản đồ như một chức năng toàn cục" haha Tôi làm chính xác điều đó trên rất nhiều chức năng tiện ích của mình. Tôi cũng sử dụng các chức năng mũi tên theo chuỗi. "Xấu xí" là chủ quan. Và bạn đã không lập luận để ủng hộ nhận xét "hành vi xấu" của mình. Nhận xét của bạn cho tôi thấy bạn có ít kinh nghiệm về lập trình chức năng. Hãy sử dụng một ngôn ngữ như Haskell, nơi mà phần dạo đầu bao gồm một đống "hình cầu" ... không ai nói rằng đó là "xấu xí" hay "thực hành xấu". Bạn chỉ không quen với nó.
Cảm ơn bạn

5
@SergeyAlaev hoặc xem xét vợt có append, map, filter, foldl, foldrtrong 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.
Cảm ơn bạn

48

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)], [])

MDN Bài viết trên flatMap


Đặc biệt, FlapMap đã chính thức có mặt ở Node 11.0.0 theo MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Carl Walsh

3
@CarlWalsh Tôi xin lỗi, tôi không thể cưỡng lại. Cái quái flapMapgì vậy? Tôi muốn làm phẳng bản đồ của mình, không phải đập nó!
ErikE

Về một chủ đề khác, điều này cuối cùng đã biến JavaScript Array's thành Monads ™ ️ 🙃
Kutyel vào

Đây là một câu trả lời cũ nhưng cách này khá kém hiệu quả, đặc biệt là trên các mảng lớn (tạo một mảng mới và sao chép tất cả phần tử trước đó ở mỗi lần lặp). Một cách khác là sử dụng concat const flatMap = (arr, ...args) => [].concat(...arr.map(...args));(hoặc sử dụng applynế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
Bali Balo

10

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.


2
Việc triển khai này có thể làm nổ ngăn xếp đối với các mảng lớn.

Nói rõ hơn, nếu tôi hiểu bạn đúng, @ftor, bạn đang nói về việc triển khai đệ quy trong liên kết tôi đã đưa, không phải liên kết tôi hiển thị trong khối mã ở cuối câu trả lời của tôi, phải không?
Alan Mimms

2
Cố gắng [].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ó.

Cảm ơn. Tôi đã thêm một ghi chú cho hiệu ứng này.
Alan Mimms

7

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), [] )

4

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]));


1

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]


Xin chào serhii, tôi đã thử mã này, khi cha mẹ chỉ có một đối tượng sẽ cho kết quả không mong muốn. `` `Array.prototype.flatMap = function (selector) {if (this.length == 1) {return selector (this [0]); } return this.reduce ((trước, tiếp theo) => (/ * đầu tiên * / selector (trước) || ​​/ * tất cả sau lần đầu * / trước) .concat (selector (tiếp theo)))}; ``
Johnny

0

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))


Gặp lỗi khi chạy mã đã đăng{ "message": "Uncaught TypeError: [1,2,3].flatMap is not a function", "filename": "https://stacksnippets.net/js", "lineno": 15, "colno": 23 }
SolutionMill

Bạn đang sử dụng broser nào? Edge, IE và Samsung Internet chưa hỗ trợ nó, bạn nhé, Edge sẽ sớm chạy trên V8. Vì vậy, để hỗ trợ những người, bạn có thể sử dụng một polyfil
bigint

Phiên bản Chrome 67.0.3396.87 (Bản dựng chính thức) (64-bit)
SolutionMill

Trình duyệt của bạn đã lỗi thời .. Bạn nên cập nhật hoặc đặt tự động cập nhật.
bigInt

0

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);

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.