flatMap
jest niesamowicie przydatny w przypadku kolekcji, ale javascript nie udostępnia go podczas posiadania Array.prototype.map
. Czemu?
Czy istnieje sposób na emulację flatMap
w javascript zarówno w łatwy, jak i efektywny sposób bez flatMap
ręcznego definiowania ?
javascript
functional-programming
Sergey Alaev
źródło
źródło
arr.reduce((arr, v) => (arr.push(...v), arr), [])
?arr.map(...).reduce(...)
)..map
ping można po prostu spłaszczyć tablicę .Odpowiedzi:
Aktualizacja:
Array.prototype.flatMap
dotarła do ES2019Jest szeroko obsługiwany w wielu środowiskach. Sprawdź, czy działa w Twojej przeglądarce, korzystając z poniższego fragmentu kodu -
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 ]
„Dlaczego nie ma Array.prototype.flatMap w javascript?”
Ponieważ programowanie nie jest magią, a każdy język nie ma funkcji / prymitywów, które ma każdy inny język. Liczy się to, że JavaScript daje możliwość samodzielnego zdefiniowania -
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))
Albo przepisanie, które zwija dwie pętle w jedną -
const flatMap = (f, xs) => xs.reduce((r, x) => r.concat(f(x)), []) const xs = [1,2,3] console.log(flatMap(x => [x-1, x, x+1], xs))
Jeśli chcesz go przedłużyć
Array.prototype
, nic Cię nie powstrzymuje -if (!Array.prototype.flatMap) { function flatMap (f, ctx) { return this.reduce ( (r, x, i, a) => r.concat(f.call(ctx, x, i, a)) , [] ) } Array.prototype.flatMap = flatMap } const ranks = [ 'J', 'Q', 'K', 'A' ] const suits = [ '♡', '♢', '♤', '♧' ] const result = ranks.flatMap(r => suits.flatMap(s => [[r, s]] ) ) console.log(JSON.stringify(result)) // [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧'] // , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧'] // , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧'] // , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧'] // ]
źródło
Array.prototype._mylib_flatMap
), A nawet lepiej używać symboli ES6, zamiast po prostu definiowaćArray.prototype.flatMap
.map
zdefiniowaną jako funkcja globalna! Drugi to po prostu zły treningappend
,map
,filter
,foldl
,foldr
wśród niezliczonych innych w domyślnej przestrzeni nazw. Nie oznacza to złej praktyki, ponieważ słyszałeś, jak ktoś powiedział „globalne są złe” lub „rozszerzający się tubylcy jest zły” - rakieta jest jednym z najlepiej zaprojektowanych języków. Po prostu nie wiesz, jak / kiedy zrobić to odpowiednio.flatMap
został zatwierdzony przez TC39 jako część ES2019 (ES10). Możesz go używać w ten sposób:[1, 3].flatMap(x => [x, x + 1]) // > [1, 2, 3, 4]
Oto moja własna implementacja metody:
const flatMap = (f, arr) => arr.reduce((x, y) => [...x, ...f(y)], [])
Artykuł MDN na flatMap
źródło
flapMap
? chcę spłaszczyć mapę, a nie machać nią!const flatMap = (arr, ...args) => [].concat(...arr.map(...args));
(lub użycie,apply
jeśli nie możesz użyć operatora spreadu) - chociaż ta wersja concat ma problemy z rozmiarem stosu wywołań dla dużych tablic, jak wspomniano w rozwiązaniu niższymWiem, że powiedziałeś, że nie chcesz tego definiować samodzielnie, ale ta implementacja jest dość banalną definicją.
Jest też to z tej samej strony github:
Oto trochę krótszy sposób korzystania z rozprzestrzeniania es6, podobnie jak w przypadku renaudtertrais - ale z użyciem es6 i bez dodawania do prototypu.
var flatMap = (a, cb) => [].concat(...a.map(cb)) const s = (v) => v.split(',') const arr = ['cat,dog', 'fish,bird'] flatMap(arr, s)
Czy któreś z tych pomogłoby?
Należy zauważyć (dzięki @ftor), że to drugie "rozwiązanie" cierpi z powodu "Przekroczenia maksymalnego rozmiaru stosu wywołań", jeśli jest wywoływane na naprawdę dużej (np. 300k elementów) tablicy
a
.źródło
[].concat(...(new Array(300000).fill("foo").map(x => x.toUpperCase())))
. Jasne, to sprawa narożna. Ale powinieneś przynajmniej o tym wspomnieć.Lodash udostępnia funkcję flatmap, która jest dla mnie praktycznie równoważna z Javascriptem udostępniającym ją natywnie. Jeśli nie jesteś użytkownikiem Lodash,
Array.reduce()
metoda ES6 może dać ten sam wynik, ale musisz odwzorować, a następnie spłaszczyć w dyskretnych krokach.Poniżej znajduje się przykład każdej metody, mapujący listę liczb całkowitych i zwracający tylko kursy.
Lodash:
_.flatMap([1,2,3,4,5], i => i%2 !== 0 ? [i] : [])
ES6 Reduce:
[1,2,3,4,5].map(i => i%2 !== 0 ? [i] : []).reduce( (a,b) => a.concat(b), [] )
źródło
Jednym dość zwięzłym podejściem jest wykorzystanie
Array#concat.apply
:const flatMap = (arr, f) => [].concat.apply([], arr.map(f)) console.log(flatMap([1, 2, 3], el => [el, el * el]));
źródło
Zrobiłem coś takiego:
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]
źródło
Mamy teraz
flatMap()
w Javascript! I jest obsługiwany całkiem dobrzeconst dublicate = x => [x, x]; console.log([1, 2, 3].flatMap(dublicate))
źródło
{ "message": "Uncaught TypeError: [1,2,3].flatMap is not a function", "filename": "https://stacksnippets.net/js", "lineno": 15, "colno": 23 }
Array.prototype.flatMap()
pojawił się w JS. Jednak nie wszystkie przeglądarki mogą je obsługiwać. Sprawdź aktualną zgodność z przeglądarkami internetowymi Mozilli .Co do
flatMap()
metody nie jest pierwszym odwzorowuje każdy element, przy użyciu funkcji zwrotnego jako argumentu, a następnie spłaszcza się wynik do nowej tablicy (2d tablicy jest teraz 1d, ponieważ elementy są spłaszczone).Oto również przykład użycia funkcji:
let arr = [[2], [4], [6], [8]] let newArr = arr.flatMap(x => [x * 2]); console.log(newArr);
źródło