Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Javascript flatMap implementation
// [B](f: (A) ⇒ [B]): [B] ; Although the types in the arrays aren't strict (:
Array.prototype.flatMap = function(lambda) {
return Array.prototype.concat.apply([], this.map(lambda));
};
@samgiles

This comment has been minimized.

Copy link
Owner Author

@samgiles samgiles commented Jun 20, 2014

Example

[0, 1, 2, 3, 4, 5].flatMap(function(x) {
    return [x, x + 1];
});

// [0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]
@philipgiuliani

This comment has been minimized.

Copy link

@philipgiuliani philipgiuliani commented Nov 24, 2015

Thank you very much! :)

@ichpuchtli

This comment has been minimized.

Copy link

@ichpuchtli ichpuchtli commented Feb 22, 2016

Haven't benchmarked. But this is my implementation in typescript.

function flatMap<T, U>(array: T[], mapFunc: (x: T) => U[]) : U[] {
    return array.reduce((cumulus: U[], next: T) => [...mapFunc(next), ...cumulus], <U[]> []);
}
@ixth

This comment has been minimized.

Copy link

@ixth ixth commented Mar 8, 2016

How it differs from this?

Array.prototype.flatMap = function(lambda) { 
    return [].concat(this.map(lambda)); 
};
@renaudtertrais

This comment has been minimized.

Copy link

@renaudtertrais renaudtertrais commented Apr 4, 2016

@ixth I think you must use apply() in order to convert the returned array of map() into the arguments of concat() :

Array.prototype.flatMap = function(lambda) { 
    return [].concat.appy([],this.map(lambda)); 
};

Without touching the prototype :

[0, 1, 2, 3, 4, 5].reduce((list,x) => list.concat([x, x+1]), []);

// [0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]
@dsacramone

This comment has been minimized.

Copy link

@dsacramone dsacramone commented Aug 9, 2016

Here is a bit of shorter way using es6 spread, similiar to renaudtertrais's - but using es6 and not adding to the prototype.

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

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

flatMap(arr, s)

@AWilco

This comment has been minimized.

Copy link

@AWilco AWilco commented Sep 7, 2016

To avoid adding an enumerable property (which will break all for (var i in []) {} statements, this adds the function as a non-enumerable property

Object.defineProperties(Array.prototype, {
    'flatMap': {
        value: function (lambda) {
            return Array.prototype.concat.apply([], this.map(lambda));
        },
        writeable: false,
        enumerable: false
    }
});
@eldarshamukhamedov

This comment has been minimized.

Copy link

@eldarshamukhamedov eldarshamukhamedov commented Sep 12, 2016

@AWilco +1. Note that writeable and enumerable both default to false, so you can leave those off.

@vojtechhabarta

This comment has been minimized.

Copy link

@vojtechhabarta vojtechhabarta commented Nov 16, 2016

Same implementation as @dsacramone with added TypeScript type annotations.
Note that mapping function can also use index and original array.

function flatMap<T, U>(array: T[], callbackfn: (value: T, index: number, array: T[]) => U[]): U[] {
    return [].concat(...array.map(callbackfn));
}
@polkovnikov-ph

This comment has been minimized.

Copy link

@polkovnikov-ph polkovnikov-ph commented Dec 14, 2016

Not mentioned is that you should never modify prototypes of default objects, which this code perfectly failed to do.

@leefsmp

This comment has been minimized.

Copy link

@leefsmp leefsmp commented Mar 6, 2017

If the stunt is performed by trained professionals that can be acceptable

@Ran-P

This comment has been minimized.

Copy link

@Ran-P Ran-P commented Mar 6, 2017

For optimization, don't use concat use : push.apply

@nick-bull

This comment has been minimized.

Copy link

@nick-bull nick-bull commented Apr 13, 2017

@mischkl

This comment has been minimized.

Copy link

@mischkl mischkl commented Jun 7, 2017

@ichpuchtli I like it, but isn't the order of the concatenation backwards?

@dsacramone gets my vote for most concise and readable, tho :)

@TrevorSayre

This comment has been minimized.

Copy link

@TrevorSayre TrevorSayre commented Jun 20, 2017

If you need to work with deeply nested arrays:

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

const flatMapDeep = (value, mapper) => {
  return Array.isArray(value) ?
    [].concat(...value.map(x => flatMapDeep(x, mapper))) :
    mapper(value);
}

const mapper = (x) => x * 11;
const flatArray = flatMapDeep(myArray, mapper); // [11, 22, 33, 44, 55, 66, 77, 88, 99]
@itrav

This comment has been minimized.

Copy link

@itrav itrav commented Mar 2, 2018

const flatMap = (a, f) => a.map(f).reduce((xs, ys) => [...xs, ...ys]); // using map first to avoid recursion in reduce

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

flatMap([1, 2, 3], x => [x, x + 1]);                                   // => [1, 2, 2, 3, 3, 4]
@eladkarako

This comment has been minimized.

Copy link

@eladkarako eladkarako commented Feb 3, 2021

/*
recursive methods (at least obvious ones) are for chumps! lets do some string manipulation instead...  
works, assuming your array does not actually includes "[" or "]" characters ¯\(◉◡◔)/¯
*/

Array.prototype.cheeky_flatMap = function(){
  return JSON.parse( "[" 
                   + JSON.stringify(this)
                         .replace(/[\[\]\,]+/g,",")
                         .replace(/(^\,|\,$)/g,"")
                   + "]"
                   );
}

Also, works in any depth...
cheeky_flatmap([[1,2],[3,4],[[[5]]]]) - [1,2,3,4,5]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment