9 ms·
Javascript Arrays and Functional Programming
- revmoo 9y agovar m = document.getElementsByTagName("body")[0], c = m.style; c.padding = '25px';
- nerdponx 9y agoJavaScript has an advantage on Python in this style, because Python lambdas are hideously ugly and JS anonymous functions are less hideously ugly. In Python it tends to be cleaner (and sometimes faster) to write list comprehensions instead of map() and filter(), especially since Python also has lazy generator comprehensions.
- pryelluw 9y agoPython actually discourages using map anf filter. Which is kind of weird to me but whatever.
- nerdponx 9y agoWell in some cases it really is more efficient. [func(x) if x in collection if cond(x)] makes a single pass over the data and gives you a list, but list(map(func, filter(cond, collection))) makes two passes and requires some extra function calls. I think the real reason is perceived legibility on the part of the Python core devs. I seem to recall a document from a while ago in which GvR himself came out in favor of list comprehension style. "Explicit is better than implicit".
- nitroll 9y agoBoth map and filter returns an iterator, so you only iterate once when creating the list.
- namanyayg 9y agoGreat article, but questionable formatting: `return {name: player.name, age: player.age }` It also seems wrong to mutate the accumulator in the reduce example, it would just be simpler to use `forEach` and use a variable defined outside if you're going to write code this way?
- drinchev 9y agoWouldn't do it with variable, since it will make your function impure [1] 1 : http://www.nicoespeon.com/en/2015/01/pure-functions-javascript/ http://www.nicoespeon.com/en/2015/01/pure-functions-javascri...
- pdpi 9y agoThat's the GP's point though: it already is impure! If you rewrite as let acc = {...} footballPlayers.reduce(...,acc) It should become clear: acc is an external variable that got mutated by the reduce.
- bestest 9y agoIndeed. A pure function should not depend on and / or mutate external variables. I'd do it like this (yay for one-ish-liners!): const playersByCountry = footballPlayers.reduce((acc, player) => { return {...acc, [player.country]: [...(acc[player.country] || []), player]}; }, {});
- drinchev 9y agoSome more practical examples // Grab unique [1,1,2,3,4].filter( ( item, index, array ) => array.indexOf( item ) === index ) // => [1,2,3,4] // Flatten [[1,2],[3,4]].reduce( ( result, item ) => result.concat(item), [] ); // => [1,2,3,4] Not sure why the author missed `sort`. // Sort [1,2,4,3].sort( ( a, b ) => a - b )
- Veedrac 9y agoThe first is slow (O(n²) where O(n) will do), and the second is very likely to be slow (probably the same).
- drinchev 9y agoThe good old question about sacrificing performance for readability. Anyway these days filter is not so bad. [1] https://jsperf.com/array-uniq-filter https://jsperf.com/array-uniq-filter
- Veedrac 9y agoAnd insertion sort is not so bad with 20 elements. It doesn't take much for it to start falling over, though. [1]: https://jsperf.com/array-unique-filter-2/1 https://jsperf.com/array-unique-filter-2/1
- _ar7 9y agoIf you wanted to do O(n) functionally, you could do [1, 1, 2, 3, 4].filter( (obj => x => { if (obj[x]) { return false; } obj[x] = true; return true; })({}), ); What's happening is that you're passing an object (hashmap / hashset) into a function that returns a filtering function, and that object is used inside the closure to track the dupes. It's still a pure function because even though you're mutating the passed in object, the filtering function is still deterministic and referentially transparent.
- zabana 9y agoAuthor here, thanks for your feedback ! I didn't mention sort because I thought I (personally) rarely use it and wanted to provide examples of more widely used array methods (namely filter reduce and map).
- seanwilson 9y agoI accept that this is a widely used phrase now but I never understood why just using map/filter/reduce and avoiding state is enough for code to be called "functional programming". Most functional languages feature pattern matching, algebraic data types, purity, currying, strong typing, type inference, recursion instead of loops and types that cannot be null. It's a completely different style of coding. Adding map/filter/reduce to an imperative language doesn't get you close to the level of robustness and ease of implementation for that safety you'd get in a functional language.
- grabcocque 9y agoBecause (a) we don't have an unambiguous definition of what functional programming is and (b) we don't have a better term for what this style of programming is. I guess it could be called combinator-oriented programming or similar.
- seanwilson 9y agoSounds more like "coding with map, reduce and filter" to me. It beats complex for-loops where mapping, reducing and filtering are all mixed in together but I wouldn't call it functional programming.
- twii 9y agoAs mentioned in one of the comments, this is nowhere near the definition of functional programming. > It beats complex for-loops Nvm. I still prefer for loops in most cases, in some way it's easier to understand for me if I read it some months later. For example the reduce function I would write something like this: getPlayersByCountry= ( footballPlayers= [], players= {} ) -> for player in footballPlayers if players.hasOwnProperty player.country players[ player.country ].push player return players playersByCountry= getPlayersByCountry footballPlayers, {France: [], England: [], Spain: []} I guess it's mere a matter of taste.
- seanwilson 9y ago
- arkadiytehgraet 9y agoTL;DR author provides an example of map, filter and reduce functions in JS. I have been wondering lately what is the value of articles like this. They do not convey any meaningful idea, they do not offer any useful insight on underlying matters; why do people even write something like this, except to litter the Internet even more? Anyone, who is distinctly familiar with functional programming or even just with the concept of higher order functions, surely knows those three basic primitives; those, who are not, would benefit much more from the basic concepts of first-class functions etc, than this random abrupt post.
- nxsynonym 9y agoWhile it may not be valuable to the vast majority of people, it was probably valuable to the author at least. The article is not ground breaking or even front page worthy, but that doesn't mean it's the litter of the Internet. At absolute worst it's an exercise in thinking about a subject and trying to explain it to others.
- Blackstone4 9y agoFrom my comment below: "Thank you for this! I've been learning JS for React and .reduce, .every and .some are new to me! Super helpful" I feel like learning is always easier in smaller bitesizes and the person wrote something very clear and digestible. Sounds like it wasn't aimed at more advanced programmers like yourself
- such_a_casual 9y agoThe blog post has tremendous value to the right audience, no doubt about that. But it's worse than worthless for its actual target audience because the article makes those people feel like they're supposed to get it, and they probably feel shitty when they don't (I did). Even though they shouldn't. No one expects you to read that blog post and get it if you've never used Map or Reduce before. It's misleading and disheartening to the target audience. It's a pretty easy fix. Just change who you're talking to.
- 9y ago
- Blackstone4 9y agoThank you for this! I've been learning JS for React and .reduce, .every and .some are new to me! Super helpful
- zabana 9y agoAuthor here: I wrote this article mainly as a note to self that I could refer to in the future and as an exercise to help me learn the concept and force me to structure my thoughts into a coherent blog post. I thought I'd share it for those of us who are more intermediate and would like to pick up a few tricks along the way. Thank you all for the positive (and not so positive feedback)
- dberhane 9y agoReading your blog allowed me to better understand the reduce function for the first time and I want to thank you for that.
- sAbakumoff 9y agoshameless plug, here is my article about ES5 array methods that looks at them in depth : https://medium.com/@sAbakumoff/a-deeper-dive-into-javascript-arrays-390f324f7482 https://medium.com/@sAbakumoff/a-deeper-dive-into-javascript...
- lostcolony 9y agoFor learning the concepts, something that I found rather enlightening was writing map/filter/every (and I guess, some or any, and really just about any function that operates on a collection), -with- a reduce. Then, write your own recursive implementation of reduce. I found it really helpful in grokking what these things did, and more broadly improved my understanding and use of functional composition
- Kiro 9y agoI don't know if I'm delirious but I remember a few years ago when articles like this were very common on HN. Then all of a sudden it felt like everyone had "learnt" it and all that got upvoted was very complicated articles explaining some esoteric JS thing. Now it seems we're back again with. Another example is the article explaining "this" which is on the front page again. It almost feels cyclic, like it's time for the new generation of programmers to learn the quirks. It's probably an incorrect observation but it feels like the front page has developed with me. Like we all started on square 1, evolved together and now it's time to graduate and leave room for the new generation.
- supermdguy 9y agoMaybe hacker news is starting to gain popularity with students, who are trying to follow the "pro hackers".
- matlin 9y agoThis hardly covers any functional patterns that javascript is capable of. You can compose functions, avoid stack overflows in tail call recursion, use immutable values, etc. I've seen articles on pattern matching with Typescript or trampolining code but this is simple a descriptive look at three basic javascript functions. If you're interested in more of a functional approach in JS take a look at Immutable.js and/or https://pattern-matching-with-typescript.alabor.me/ https://pattern-matching-with-typescript.alabor.me/.
- guiomie 9y agoIn his example Array.prototype.reduce, isn't this almost the same as just doing a foreach loop? I don't understand why doing this, it's pretty much the same number of lines.
- ablomen 9y agoIn this example it is, but a nice thing about using map, reduce etc is that you can chain them together, ex: let out = ["a", "b", "c", 1, 2, 3] // Turn letters to upper case .map(i => { return typeof i === "string" ? i.toUpperCase() : i; }) // Add one to numbers .map(i => { return typeof i === "number" ? i + 1 : i; }) // split letters and numbers up .reduce((all, i) => { typeof i === "string" ? all.letters.push(i) : all.numbers.push(i); return all; }, { "letters": [], "numbers": [] }); // => { letters: ["A", "B", "C"], numbers: [2, 3, 4] }
- Veedrac 9y agoOr... you can do all of that in a 4-line loop, without any of the harm to readability or additional code-bloat that's especially important for websites.
- moosingin3space 9y agoThe problem with this is that it generates two intermediate arrays in the maps, then effectively drops them in the reduce. This may be inefficient. I wish that JavaScript's map/filter/reduce functions returned lazy iterators, like in Rust, so that code like this doesn't produce intermediate arrays. Does anyone know of a library that provides this?
- marmart 9y agoThose intermediate iterations could be dropped by composing the map functions together (same with the reduce), if that really was a performance bottleneck.
- 9y ago
- sleazybae 9y agothis post introduced me to the arrow function ((https://stackoverflow.com/questions/24900875) https://stackoverflow.com/questions/24900875)), which i'd previously not heard of. evidently it was introduced in ECMAscript 6 but for engines that use JavaScript and older browsers (amazingly, they still exist) it won't work
- dave7 9y agoThere is a spurious ) ending your link, resulting in page not found error. For those using arrow functions these days for web, most will use Babel in the build process to provide compatibility with older platforms.
- jaunkst 9y agoFor more fun with FP checkout lodash fp https://github.com/lodash/lodash/wiki/FP-Guide https://github.com/lodash/lodash/wiki/FP-Guide Also read about using flow over chain
- cygned 9y agoAs a side note, using .push() isn't something I would consider being functional because immutability is a core concept of functional programming. Too bad, JavaScript makes it hard to do it that way, in that concrete example using Object.assign() and .concat().
- jlg23 9y ago> immutability is a core concept of functional programming. That depends very much on your definition of FP (or how "pure" the FP is, depending on your definition of "purity").
- cygned 9y agoFair point. All my touch points (Lisp, JavaScript, even a bit Haskell and several books) all proposed immutability as being a fundamental principle. Would you mind expanding a bit on that point in case I totally misunderstood something?
- jlg23 9y agoYou did not "totally misunderstand something": Immutability is a virtue in FP, you should write in this style. But it is only a "fundamental principle" and "core concept" in highly pure languages - and even Haskell allows mutable variables[1]. Sometimes you just need them, e.g. when handling potentially very large vectors in finite memory. CL's (sort ...) for example mutates the input sequence (or "destructively sorts" it [2]). [1] https://wiki.haskell.org/Mutable_variable https://wiki.haskell.org/Mutable_variable [2] http://www.lispworks.com/documentation/HyperSpec/Body/f_sort_.htm http://www.lispworks.com/documentation/HyperSpec/Body/f_sort...
- qrohlf 9y agoagreed, the Array.prototype.reduce could be accomplished in a far more readable way using a simple forEach. If the function was doing a `concat` instead of a `push`, it would make a lot more sense to me as a functional example, because then the author would actually be taking advantage of the way the accumulator result from the previous call is passed into the next call.
- deleted 9y ago[deleted]
- sbr464 9y agoI noticed you used let to instantiate variables. Should switch to const, and if really going functional, will begin to find few rare cases where let is needed.