1. 5
  1.  

  2. 8

    The imperative and declarative versions are, in two cases, semantically different.

    Execute something on every element with map: the for-loop doesn’t do anything with the return value from performSomething, whereas map puts the results into an array. Consider using forEach instead of map. e.g. array.forEach(performSomething)

    Finding a single element in the array: the use of a return in the for-loop causes an early exit, whereas filter will iterate through the entire array.

    Iterate over an array to count a property of each item: I can only nit-pick and say the declarative implementation as-written is more verbose than the imperative one. Please consider: items.reduce((result, item) => result + item.content.value, 0)

    1. 3

      Finding a single element in the array: the use of a return in the for-loop causes an early exit, whereas filter will iterate through the entire array.

      There’s also Array.prototype.find().

      With regards to this improvement:

      items.reduce((result, item) => result + item.content.value, 0)

      If you want to get (probably too) fancy with destructuring, you could do:

      items.reduce((result, { content: { value } }) => result + value, 0)

      1. 1

        items.reduce((result, { content: { value } }) => result + value, 0)

        That’s clever, thanks I will include this example!

        1. 1

          There’s also Array.prototype.find().

          Is Array.prototype.find() ES5?

          1. 1

            I’m still not sure what aligns to ES5 or ES6, but as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find:

            This method has been added to the ECMAScript 2015 specification and may not be available in all JavaScript implementations yet.

            1. 2

              ES6 and ES2015 are two names for the same thing. ES5 would be ES2009 under the new naming scheme.

        2. 1

          Good points, thanks for the feedback, I’m editing the post to incorporate it

        3. 5

          @flaviocopes i think there are lots of minor inaccuracies in here which make it less valuable as a reference / learning piece. Sorry I can’t list them all, but the code smell for me in the writing is that there is not a super clear description of what map, filter and reduce do, and so the examples are a little arbitrary / fuzzy, or are not really the ‘general’ cases that illustrate each method, and this suggests that maybe you haven’t fully understood them. hope that’s helpful, ping if you do any rewrite and want review.

          1. 3

            map and filter capture common operations in a really solid way that is almost always an advantage over a loop.

            In my experience, the bar is a lot higher when considering rewriting loops into reduces. A reduce isn’t necessarily much more opinionated or explicit than a loop, and so you need to be careful to ensure that you’re choosing it over a loop for the right reasons.

            If your reducer body is longer than a single statement, or your output data structure is more complicated than a single literal, you might not be gaining much by using reduce.

            1. 2

              After writing golang for 3+ years now, seeing one-liners for list operations makes me super wary. What if i need to inspect things as the loop is iterating? How is it iterating? Is it doing an allocation for an array iterator? Am I re-using memory or is it creating a new object for each thing in the array?

              Maybe I just have stockholm syndrome from golang but there is something to be said for writing a little extra code (albeit, a lot of extra code over time) for the sake of clarity and maintainability.

              1. 2

                What if i need to inspect things as the loop is iterating?

                I’m not sure exactly what you mean by this but if you need to do something map et al don’t do, then don’t use them. The value of them is that if you do use them, a reader knows what you can’t do, and know what you can’t do is very powerful.

                How is it iterating?

                The idea is you shouldn’t care, just that it does. In general, map et al are meant to be implemented efficiently because they should be used by everyone.

                Is it doing an allocation for an array iterator? Am I re-using memory or is it creating a new object for each thing in the array?

                In language with a functional pedigree, no because the values are immutable. In a language like Go, this would have to be specified but the type should tell you that, right? If it’s a pointer it’s to the original value.

                Maybe I just have stockholm syndrome from golang but there is something to be said for writing a little extra code (albeit, a lot of extra code over time) for the sake of clarity and maintainability.

                The counter is that all those for loops are less clear and less maintainable because you can do anything in them. As I said above, the value of map and friends is what you cannot do.

                1. 1

                  In language with a functional pedigree, no because the values are immutable. In a language like Go, this would have to be specified but the type should tell you that, right? If it’s a pointer it’s to the original value.

                  Golang range re-assigns iteration variables.

                  1. 2

                    So those are just semantics you have to know about your language and libraries. Being a one-liner doesn’t change that.

                2. 1

                  What if i need to inspect things as the loop is iterating?

                  Do golang debuggers not let you set breakpoints inside closures?

                3. 1

                  I’ve thought about doing this a few times, but from what I read, especially for more intensive use-cases, this functional style is just slower, without having a real advantage, beside syntactic sugar. In fact, older systems might not even support it, which is especially inconvenient when maps, filters, and reduces (despite their simplicity and prettiness) are used in critical components of the software.

                  1. 1

                    I’ve thought about doing this a few times, but from what I read, especially for more intensive use-cases, this functional style is just slower, without having a real advantage, beside syntactic sugar.

                    What did you read? According to both tests I ran now and every JS performance website I could find, forEach and for-loops perform the same in modern Javascript VMs.

                    In fact, older systems might not even support it, which is especially inconvenient when maps, filters, and reduces (despite their simplicity and prettiness) are used in critical components of the software.

                    As @flaviocopes points out, map / filter / reduce are all in ES5. What level are compatibility are we talking about here?