为什么在javascript中没有Array.prototype.flatMap?


81

flatMap在集合上非常有用,但是javascript不能提供一个Array.prototype.map。为什么?

有没有办法以flatMap简单有效的方式在javascript中进行仿真,而无需flatMap手动定义?


10
“为什么?” 因为还没有人提出建议?这是提出新功能的方法“有没有什么方法可以模拟flatMap ...而无需手动定义flatMap?” 嗯 我不明白 你是说喜欢arr.reduce((arr, v) => (arr.push(...v), arr), [])吗?
Felix Kling

(^这只是扁平的部分,所以我想应该是arr.map(...).reduce(...))。
菲利克斯·克林

您可以在ping后将阵列弄平.map
kennytm

1
嗯,您想“模仿”它而不是“定义”它。那是什么意思?

8
它将很快成为JS的一部分。tc39.github.io/proposal-flatMap
Vijaiendran's

Answers:


95

更新: Array.prototype.flatMap正在本机ECMAScript上。目前处于第4阶段

它在许多环境中得到广泛支持。使用下面的代码片段查看它是否在您的浏览器中有效-

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 ]


为什么在javascript中没有Array.prototype.flatMap?

因为编程不是魔术,每种语言都没有其他每种语言都具有的功能/原语

重要的是

JavaScript使您能够自己定义它

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

或将两个循环合为一个的重写

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

如果您想在上进行操作Array.prototype,则没有什么可以阻止您

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
最好对原型的任何附加内容(例如Array.prototype._mylib_flatMap)进行命名空间,甚至更好地使用ES6符号,而不是简单地定义Array.prototype.flatMap
王凤阳

11
@FengyangWang “认为更好”是非常主观的。如果这是您自己的应用程序,并且您有扩展原型的理由,则没有任何问题。如果这是您要分发给他人使用的库/模块/框架,那么我会同意您的观点。但是,评论不是讨论此问题的地方-应提供比评论中应提供的更多的解释和细节。
感谢您

首先是丑陋的:将FlatMap定义为函数会使代码一团糟-想象一下将函数代码map定义为全局函数!第二只是不好的做法
谢尔盖·阿拉维

3
@SergeyAlaev回复:“将地图想象为全局函数”哈哈,我在很多实用程序函数中都做到了这一点。我也使用箭头功能序列来咖喱它们。“丑陋”是主观的。您也没有理由支持您的“不良做法”评论。您的评论向我表明您对函数式编程的经验很少。像Haskell这样的语言,其前奏包括大量的“全局变量”……没有人说这是“丑陋的”或“不好的作法”。您只是不熟悉它。
谢谢您

5
@SergeyAlaev或考虑球拍有appendmapfilterfoldlfoldr中无数人在默认的命名空间。这并不是一个坏习惯,因为您听说有人说“全球人是坏人”或“扩展本地人是坏人”-球拍是设计最好的语言之一。您只是不知道如何/何时适当地进行。
谢谢您

48

flatMap已被TC39批准为ES2019(ES10)的一部分。您可以像这样使用它:

[1, 3].flatMap(x => [x, x + 1]) // > [1, 2, 3, 4]

这是该方法的我自己的实现:

const flatMap = (f, arr) => arr.reduce((x, y) => [...x, ...f(y)], [])

关于FlatMap的MDN文章



3
@CarlWalsh对不起,我无法抗拒。到底是flapMap什么?我想展平我的地图,而不是拍平它!
ErikE

在另一个主题上,这最终将JavaScript数组变成了Monads™️🙃–
Kutyel

这是一个旧的答案,但是效率很低,尤其是在大型数组上(创建新数组并在每次迭代时复制所有先前的元素)。另一种方法是使用concat const flatMap = (arr, ...args) => [].concat(...arr.map(...args));(或使用apply您不能使用散布运算符的方法)-尽管此concat版本在
较大的

10

我知道您说过您不想自己定义它,但是此实现是一个非常简单的定义。

在同一个github页面上也有这个:

这是使用es6扩展的较短方法,类似于renaudtertrais的方法-但使用es6而不添加到原型中。

var flatMap = (a, cb) => [].concat(...a.map(cb))

const s = (v) => v.split(',')
const arr = ['cat,dog', 'fish,bird']

flatMap(arr, s)

这些都有帮助吗?

应该指出的是(由于@ftor),如果在非常大的数组(例如300k元素)上调用,则后一个“解决方案”将遭受“超出最大调用堆栈大小”的困扰a


2
此实现可能会炸毁大型阵列的堆栈。

为了清楚起见,如果我对您的理解正确,@ ftor,您是在说我给出的链接中的递归实现,而不是答案末尾在代码块中显示的那个,对吗?
艾伦·米姆斯

2
尝试[].concat(...(new Array(300000).fill("foo").map(x => x.toUpperCase())))。当然,这是一个极端的案例。但是您至少应该提到它。

谢谢。我为此添加了注释。
艾伦·米姆斯

7

Lodash提供了flatmap函数,对我而言,它实际上等效于Javascript本机提供的功能。如果您不是Lodash用户,那么ES6的Array.reduce()方法可以为您提供相同的结果,但是您必须以离散的步骤进行映射然后展平。

下面是每种方法的示例,它们映射一个整数列表并仅返回几率。

Lodash:

_.flatMap([1,2,3,4,5], i => i%2 !== 0 ? [i] : [])

ES6减少:

[1,2,3,4,5].map(i => i%2 !== 0 ? [i] : []).reduce( (a,b) => a.concat(b), [] )

4

一种相当简洁的方法是利用Array#concat.apply

const flatMap = (arr, f) => [].concat.apply([], arr.map(f))

console.log(flatMap([1, 2, 3], el => [el, el * el]));


1

我做了这样的事情:

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]


嗨,serhii,我尝试过这段代码,当父对象只有一个对象会产生意外结果。Array.prototype.flatMap = function(selector){if(this.length == 1){返回选择器(this [0]); }返回this.reduce((上一个,下一个)=>(/ * first * / selector(prev)|| / * all first * / prev之后。concat(selector(next)))}; ```
约翰尼

0

我们现在有了flatMap()Java语言!而且它支持得很好

flatMap()方法首先使用映射函数映射每个元素,然后将结果展平为新数组。它与map()相同,后跟深度为1的flat()

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

您正在使用什么兄弟?Edge,IE和Samsung Internet尚不支持它,但是Edge将很快在V8上运行。因此,为了支持这些,你可以使用一个polyfil
BIGINT

Chrome版本67.0.3396.87(正式版本)(64位)
SolutionMill

您的浏览器已过时。您应该对其进行更新或设置为自动更新。
bigInt

0

Array.prototype.flatMap()现在已经到达JS。但是,并非所有的浏览器都可能支持它们,请检查Mozilla Web文档的当前浏览器兼容性。

flatMap()方法的作用是,首先使用回调函数作为参数映射每个元素,然后将结果展平为新数组(由于元素被展平,因此2d数组现在为1d)。

这也是如何使用该函数的示例:

let arr = [[2], [4], [6], [8]]

let newArr = arr.flatMap(x => [x * 2]);

console.log(newArr);

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.