Skip to content

Instantly share code, notes, and snippets.

@samgiles
Created June 20, 2014 11:32
Show Gist options
  • Save samgiles/762ee337dff48623e729 to your computer and use it in GitHub Desktop.
Save samgiles/762ee337dff48623e729 to your computer and use it in GitHub Desktop.
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
Copy link
Author

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
Copy link

Thank you very much! :)

@ichpuchtli
Copy link

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
Copy link

ixth commented Mar 8, 2016

How it differs from this?

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

@renaudtertrais
Copy link

@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
Copy link

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
Copy link

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
Copy link

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

@vojtechhabarta
Copy link

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

@reverofevil
Copy link

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

@leefsmp
Copy link

leefsmp commented Mar 6, 2017

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

@Ran-P
Copy link

Ran-P commented Mar 6, 2017

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

@nick-bull
Copy link

@mischkl
Copy link

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
Copy link

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
Copy link

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]

Copy link

ghost 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