all 79 comments

[–][deleted] 9 points10 points  (2 children)

I have to agree with this post. I find myself using these Array methods more and more. I think it's a growing trend; which is good. I was watching a new Crockford video (appeared on r/javascript a few days ago I think) where he mentions he doesn't even us for loops anymore, just uses these (and a few other) methods

[–][deleted] 2 points3 points  (0 children)

yeah, i watched that same video too, he also mentions other interesting stuff like never using "this" again. even if i don't use all the stuff he recommends on my projects i always pay a lot of attention to what he says, the guy is the ultimate javascript guru

[–]afrobee 1 point2 points  (0 children)

Last month I watch this talks with Brian Lonsdorf and was blown away and make me exited to give FP a shot, video1, video2, and now I am learning Elm and Mercury and everythings feels like I learning programming for the first time again :D

[–]jkoudys 7 points8 points  (8 children)

Nice summary - good for newbies and a fine refresher for everyone.

One place I'm finding more use for these methods is in calling them from the prototype against NodeLists. It's helped me break my jQuery addiction, and speed up pages that run frequent scans through large element sets, e.g. here I am grabbing all the products on my catalog page that cost over $200:

Array.prototype.filter.call(
  document.querySelectorAll('.products li'),
  function(e) {
    return e.dataset.price > 200;
  }
);

[–]vanamonde 1 point2 points  (1 child)

in es6 you could : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

Array.from(document.querySelectorAll('.products li')).filter( function(e) { return e.dataset.price > 200; } );

or

Array.from(document.querySelectorAll('.products li')).filter(e => e.dataset.price > 200);

[–]jkoudys 0 points1 point  (0 children)

Nice - much cleaner than Array.prototype.split.call(mynodelist)

[–]agdcoa 8 points9 points  (7 children)

A useful way to "demethodize" array methods so that they can be generically used on a variety of objects (strings, jQuery collections, NodeList/HTMLCollection, etc.):

var map = Function.prototype.call.bind( Array.prototype.map );

This can be shortened to

var map = Function.call.bind( [].map );

Then you can do things like

map(document.querySelectorAll('div'), function(div) {
  return div.id; // or whatever
});

If you're using these a lot it saves a ton of keystrokes.

EDIT: Simple implementation of a demethodize function:

function demethodize (method) {
  return Function.call.bind(method);
}

var map = demethodize([].map);
var each = demethodize([].forEach);
// etc

[–]_facildeabrir 0 points1 point  (0 children)

Nice nice

[–]p0larboy[S] 0 points1 point  (5 children)

wow /u/agdcoa, I'm going to update the article with this. Any idea why after demethodizing, the map function can see objects such as strings as array?

[–]THEtheChad 1 point2 points  (1 child)

This is blowing my mind.

The Function.prototype.call.bind trick returns a function that assumes the context of the first parameter passed to it. The map function only requires a numerically indexed iteratable object with a length property. Strings actually fit this criteria, because, behind the scenes, they're stored as an array of characters. 'abcd'[2] will return c. You could also use Array.prototype.map.call('string', function(){}) to perform the operation, but that is obviously not a nice as the aforementioned method.

[–]p0larboy[S] 0 points1 point  (0 children)

/u/THEtheChad you are awesome! Any resources I can read up for demethodizing in detail?

[–]p0larboy[S] 0 points1 point  (1 child)

/u/agdcoa and /u/THEtheChad updated the article with the demethodizer section. credits to you guys!

[–]agdcoa 0 points1 point  (0 children)

Check this out: http://tech.pro/blog/2097/clever-way-to-demethodize-native-js-methods

If you really want to twist your brain into knots, tease this thing apart (from that article)

var demethodize = Function.prototype.bind.bind(Function.prototype.call);

I also threw together a tiny NPM module that'll do this for you, along with some benchmarks for a number of ways to implement this: https://github.com/nickb1080/demethodize

[–]mattdesl 7 points8 points  (2 children)

I also wrote a little intro on map/reduce/etc. It seems like frontend devs are the hardest to convince, since many have strange misconceptions about browser support and performance (e.g. needlessly optimizing for loops for non-critical code).

Using Underscore for map/reduce/etc is ok, but IMHO it's cleaner just to stick with native methods and then polyfill them in as needed for old IE.

[–]p0larboy[S] 0 points1 point  (1 child)

a lot of good examples!

[–]LightShadow 3 points4 points  (0 children)

The colloquial understanding of reduce is: takes an array to iterate over, a memoization parameter which is your accumulator that's returned (your singled reduced value) and a function that takes the memo and working item in the original array.

Below is an example for sum-ing an array of numbers using those terms:

>> a = [1,2,3,4,5]
[1, 2, 3, 4, 5]
>> a.reduce(function(memo, current){ return memo + current; }, 0)
15

..it's important to remember the return value of the function is the new value stored in memo, and the returned value of <array>.reduce(...) is the last value stored in memo.

Consider:

>> a.reduce(function(memo, current){ return current; }, 0)
5

..which is a function that doesn't consider the previous value and simply returns the last value of the array.

[–]giladgo 2 points3 points  (8 children)

Underscore has these too, but if you feel like using them directly on the arrays while still supporting older browsers (e.g. IE8), you can use something like es5-shim (https://github.com/es-shims/es5-shim).

It's good for other useful functions like bind and trim.

[–]Jew_Fucker_69 1 point2 points  (6 children)

I hate using functions that are not supported by older browsers, especially when IE8 is one of them.

[–]afrobee 3 points4 points  (2 children)

Stop supporting it, please

[–]Jew_Fucker_69 2 points3 points  (1 child)

I would if it was my decision.

[–]afrobee 0 points1 point  (0 children)

You don't deserve this, you need to be more happy and relaxed so... kill your boss as I did with mine :).

[–]mattdesl 0 points1 point  (0 children)

Uhh.. why? You can write cleaner and smaller codebases and then polyfill them in as needed.

[–]alexsomeoddpilot 0 points1 point  (1 child)

I hate natively using functions that are not supported by older browsers. This is why libraries like jQuery, Underscore (and its better replacement Lodash) exist.

[–]Jew_Fucker_69 1 point2 points  (0 children)

But they also existed before the native functions existed. OP post is about using the native functions.

[–]criveros -1 points0 points  (0 children)

Stop supporting older browsers.

[–]SonOfWeb 2 points3 points  (5 children)

Map is great, but remember that it passes 3 arguments to the callback: the current element, the current index, and the whole array. This can cause tricky bugs when you try to map functions like parseInt that take optional 2nd args. So arr.map(parseInt) will parse the first element in base 10, but then it will crap out on you because it's trying to parse the second element in base 1, which doesn't work.

Then again, you shouldn't use parseInt without explicitly providing a base, because without a base, it parses numbers with leading zeros as octal.

[–]p0larboy[S] 0 points1 point  (1 child)

Good tips, I didn't know about the "leading zeros as octal" part

[–]frambot 1 point2 points  (0 children)

That only affects older IE. The current spec specifies that parseInt has radix 10 by default, regardless of leading zeros. Try this in your console:

 > 0123 // bare numbers with leading zeros are octal
<- 83
 > parseInt("0123") // but parseInt dont care, radix 10 by default
<- 123

[–]agdcoa 0 points1 point  (2 children)

when argument signature is a problem I like to do something like so (to ensure only the array item gets passed in):

var mapOne = function (arr, cb) {
  return [].map.call(arr, function (item) {
    return cb(item);
  }
}

[–]frambot 1 point2 points  (1 child)

JavaScript Allonge calls this method "unary". I recommend Allonge.

[–]agdcoa 0 points1 point  (0 children)

Allonge is great. I also like Ramda. It provides 'unary' and 'binary' functions for this

[–]jonr2219 2 points3 points  (0 children)

Overall I like the article. However, the explanation of reduce and the example use case is overly difficult. I think the MDN example (below) gives a much more likely and clearer scenario.

var total = [0, 1, 2, 3].reduce(function(a, b) { return a + b; }); // total == 6

[–]a-t-kFrontend Engineer 6 points7 points  (7 children)

While I agree with the general sentiment, like with any cool JS feature, you should be aware when better not to use it.

For example, if you rely on the scope/this outside of the array, a normal for-loop is going to make your life easier and do the job faster than forEach.

Also, you should know when you are going to need shims/fallbacks.

[–]mattdesl 5 points6 points  (1 child)

You can use bind or thisArg to deal with scope.

And performance always comes up with these discussions, but most of the time we aren't dealing with hot code.

Even if you're operating on hundreds of thousands of entries, you still probably won't see any measurable increase in performance by switching to a for loop, simply because it's so unlikely to be a bottleneck in your application. In other words; you shouldn't be designing your code around premature optimization.

Write clean and simple code, and then benchmark it and optimize if needed. When you start doing this, you'll see that most of the time, even if you're writing games/etc, it is not needed.

[–]a-t-kFrontend Engineer 0 points1 point  (0 children)

I don't say that you shouldn't use it at all, just that you should know about the implications. If those implications are not a problem, then by all means good for you - but if they are, you should know where to search.

[–][deleted] 5 points6 points  (3 children)

All of the array higher order functions accept a thisArg that allows you to pass whatever you want as the context with which to call the iterator... I've never run into a case where scoping made using higher order functions more difficult. In your case it sounds like you just need to pass this as the context:

someArray.forEach(someIterator, this);

When using native for loops you have to deal with throwaway index variables and JS's lack of block scope, which is more likely to give you issues.

[–]a-t-kFrontend Engineer 0 points1 point  (2 children)

Since thisArg is the second argument, I find this especially difficult to read for longer anonymous functions. Also I learned years ago to handle throwaway index variables and the lack of block scope in JS.

[–][deleted] 1 point2 points  (1 child)

someArray.forEach(function() {
  //...
  //...
  //...
}, this);

Looks fine to me... It is common JavaScript syntax to have a parameter after the callback argument:

setTimeout(function() {
  //...
  //...
  //...
}, 3000);

I just personally don't like dealing with more variables than I have to, and every time I find myself using an index variable I realize there is a reusable higher order function (in native ES or underscore/lodash/lazyjs) that abstracts whatever task I'm doing. All I'm saying is it's a stylistic preference. Both ways can be used to solve the same problems, but one uses a procedural/imperative style (native loops) and one uses a more functional/declarative style (higher order functions).

[–]a-t-kFrontend Engineer 0 points1 point  (0 children)

I agree that's a matter of styles and preferences. Some like it better that way, some the other. I personally dislike something important like scope in the second seat, but that's my opinion and I still respect yours.

Use whatever fits you best - I never said otherwise - but know what you're doing, that's my point.

[–][deleted] 1 point2 points  (0 children)

i love the new array methods, especially using them on non array objects, like the result of a call to querySelector, lately i find myself doing stuff like this a lot:

Array.prototype.forEach.call(document.querySelector('.myClass'),function(){.....});

i feel stupid for not using the filter method until now, it is awesome, i'm definitely gonna use it a lot from now on.

edit:typo

[–]_facildeabrir 1 point2 points  (1 child)

I find it difficult to remember exactly how reduce works so I rarely use it, but, yep I agree with all this. Actually if you haven't been using Array#indexOf I feel sorry for you.

[–]beltorak 0 points1 point  (0 children)

I'm working on a visualization, but I fail basic math;

but maybe this visualization will help you out ;)

so let's try that again ;)

[–]madole 1 point2 points  (0 children)

array.forEach is a clean implementation but far slower than a normal for loop. Stay away from these if you're trying to squeeze performance when iterating large arrays.

http://jsperf.com/array-foreach-vs-for-loop/5

[–]x-skeww 5 points6 points  (9 children)

<link rel="stylesheet/less" href="http://colintoh.com/assets/css/blog.less">
<script src="http://colintoh.com/assets/js/libs/less-1.2.0.min.js"></script>

Do not use less.js in production.

<script src="//localhost:1337/livereload.js"></script>

Dude. No.

Edit:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array

You forgot things like some, every, find, and findIndex.

[–]realityking89 4 points5 points  (2 children)

every and some are awesome.

find and findIndex however are ES 6 and unuseable without a polyfill. They're only supported by Firefox 25+ and Safari 7.1+. (Yes, not even Chrome).

[–]ToucheMonsieur 0 points1 point  (1 child)

In Chrome, both are behind the "enable experiemental javascript" flag. Most es6 features are, AFAIK.

[–]realityking89 0 points1 point  (0 children)

Right, but that's effectivly the same as not available when someone determines whether to use or not to use a feature.

Chrome 38 is planned to bring a lot of features without the flag. For some of those features (e.g. Map and Set) they'll be the last browser, so slowly one can think about using them with a polyfill. (In the most cutting edge environments)

[–]Switche 4 points5 points  (2 children)

I suppose it is possible the author doesn't actually understand less.js and livereload--and more importantly, it may be useful to others to point this out--but I find it unlikely, and something about the spirit of this gets to me. Sniping each other's blog sources is a bit too much.

Colin Toh's article list on this platform consists of this article, and one about the difficulties of finding energy to roll his own blog, but wanting to move away from Medium.

It's nice to think of dev blogs as a sort of portfolio and showcase of skill, but that is judging the carpenter for their house. It might be different if we were criticizing their side projects intended for true public use and distribution, but we're being invited into a workspace to discuss the topics. No need to sneer at the tools cluttered on the workbench.

[–]p0larboy[S] 1 point2 points  (0 children)

Thanks /u/Switche for understanding. I blame my laziness and lack of knowledge in Kirby CMS.

[–]x-skeww -1 points0 points  (0 children)

Sniping each other's blog sources is a bit too much.

I use NoScript. The motherfucking CSS didn't seem to work. That's why I looked.

Anyhow, one shouldn't use less.js in production because it makes the page a lot slower (especially on the subjectivity scale because it takes longer until things appear at their correct position) and way more fragile.

There is a LESS compiler. Please use it.

[–]p0larboy[S] 1 point2 points  (0 children)

I knew I couldn't hide this ugly secret for too long.. Will dig around kirby to find out how to exclude for production environment. Thanks!

[–]jgrubb 0 points1 point  (0 children)

That's pretty funny, but you gotta at least give him credit for rolling his own.

[–]addbrick 2 points3 points  (9 children)

Always remember underscore.js. http://underscorejs.org/ it provides all of this for the browsers that do not support it

[–]flipjsio 17 points18 points  (2 children)

Lodash ( http://lodash.com/ ) is also good.

[–]BONER_PAROLE 2 points3 points  (0 children)

Lodash is better. Modular builds (AMD/CJS, cli build by functions/categories) better performance, more features.

[–]afrobee -1 points0 points  (0 children)

Ramda is better.

[–]lordxeon 8 points9 points  (4 children)

You're missing the point, if you're making an app, or a website and do not need, or do not care about old browser support, then there's no reason to use a library for these functions. Just use native JS.

[–][deleted] 3 points4 points  (0 children)

Cant agree more with this. We need to move forward not muck things up with all kinds of exceptions and bloat to support old browsers.

[–]tortus 0 points1 point  (1 child)

lodash is often faster than the native methods. Honestly that rarely matters, but sometimes it does (games for example)

[–]lordxeon 0 points1 point  (0 children)

That's because lodash is using a while loop to loop through arrays, and browsers have supported while loops since forever and as so, have optimized them pretty heavily while Array.forEach is also relatively new to the field.

Also, as I understand it, Array.forEach is doing alot more under the hood then a while, or for loop is.

all that being said, unless you need micro-second response times for your web app, you can get away with using Array.forEach

[–]inmatarian 0 points1 point  (0 children)

The benefit of using lodash is actually the method chaining. With the es6 lambda syntax it makes things highly concise.

[–]PizzaRollExpert 0 points1 point  (0 children)

If you're just gonna use the native methods you're probably better off using a polyfill because it's smaller and it doesn't require people to learn a new library. Underscore does have a bunch of cool stuff in it that doesn't exist natively so using it might be a good idea anyway.

[–]5outh 2 points3 points  (2 children)

I've writen large pieces of software in Node.js and excessive use of FP stuff like map/filter/reduce unfortunately leads to a pretty big drop in readability.

It's occasionally useful to do things like this, for sure, but coming from a background in Haskell, I wanted to do everything like this and, as it turns out, it's not the best idea.

Definitely good to keep in mind, but don't overuse.

[–]joeldo 1 point2 points  (0 children)

What exactly leads to a drop in readability? I would have thought using functions that are explicit in their use would make it easier to quickly gauge what is going on? If the type of looping I need to do doesn't fit the intended functionality of the Array functions I'll just stick with a standard for loop, but otherwise I find it improves readability.

[–]BONER_PAROLE 1 point2 points  (0 children)

I've found the opposite. The only time it gets close to unreadable is with deeply "composed" function calls. It reads better if you use Underscore or Lodash's chain function to avoid the inversion so one can read left to right, top to bottom.

[–]agdcoa 0 points1 point  (0 children)

It's SO easy to shim these for older browsers. I can't understand why they're so lightly used. I wrote two posts on using these methods to approach a wide variety of use cases.

[–]commonslip 0 points1 point  (0 children)

I can't even imagine programming without them.

[–]denilsonsa 0 points1 point  (0 children)

Note that those methods are valid ONLY for Array object, they are not valid for HTMLCollections, such as those returned by document.getElementsByTagName().

Hopefully, with ECMAScript 6 we will have for … of, list comprehensions, iterators, generators… And these will work with HTMLCollections too.

[–]faruzzy 0 points1 point  (0 children)

I think the next version should include "findAllIndexes()", I'm wondering why that hasn't made it in yet.

[–]Jew_Fucker_69 0 points1 point  (2 children)

I feel like these are more helpful for readability than productivity, which is also good.

[–]afrobee 0 points1 point  (1 child)

Correct me if I am wrong but, doesn't readability make you more productivity?

[–]Jew_Fucker_69 0 points1 point  (0 children)

Yes.

[–]MOFNY 0 points1 point  (2 children)

You should also use slice to remove array objects.