DEV Community

Nicholas Kircher
Nicholas Kircher

Posted on

How to code without loops - ever!

OH YEAH! It's a good day for some knowledge! It's time to talk about loops --- it's time to talk about loops --- it's time to talk about loops --- and how you can throw them out the window and replace them with something better! Prepare for the Defenestration of JavaScript ("Defenestration" means "to throw out the window").

I very often see - in my travels - code that looks like this:

const items = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth'];

let foundItem;

for (item in items) {
  if (item.startsWith('s')) {
    foundItem = item;
  }
}
Enter fullscreen mode Exit fullscreen mode

So if you're familiar with JavaScript/Typescript, which you are, this code should be fairly straight forward to understand. We're looking for an item in a list of strings where the first letter is 's', and if we find it, we assign it to foundItem. This is a "find" operation. We're looking for a specific element which matches our predicate condition. We only need one item, and once we've found it, we don't need to keep looking through the rest of the list. Therefore, our foundItem should be the string "second".
Right?

If you're eagle-eyed, you might notice that we are not stopping at the first matching element. We keep going. "Ok so lets add a break statement". Oh now we're really getting right into command and control here. What if I told you that all the code in that above example can be written in 2 lines, without simply compressing it, and without any mutation or variable reassignment, and stops searching after the first match?

const items = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth'];
const foundItem = items.find((item) => item.startsWith('s'));
Enter fullscreen mode Exit fullscreen mode

Welcome to the wonderful world of iteration methods! There is not a single thing that I can do with loops - EXCEPT IN INFINITE GENERATOR FUNCTIONS, DON'T @ ME - that I can't with simple iteration methods that are built-in.

Ever seen this kind of thing?

let newStruct = {};

for (item in items) {
  newStruct[item] = generateSomeRelevantDataFor(item)
}
Enter fullscreen mode Exit fullscreen mode

Mutations are supremely dangerous. Even if you think you know exactly what is going to happen here, that can very easily change. All someone has to do is pass in an object from somewhere else - like a function argument - as the initial value for newStruct and now you are mutating a reference shared by who knows how many other things, and it changes for all of them. Also, doing things immutably is hip and fashionable, so try this instead:

const newStruct = items.reduce(
  (result, item) => ({ ...result, [item]: generateSomeDataFor(item) }),
  {}
)
Enter fullscreen mode Exit fullscreen mode

This will build up your new object (each version of which is provided as "result") as it iterates over the items list, and we're doing it immutably. Spread operators don't directly copy that section of memory into a duplicate block of memory, either. This is all done by reference, so it's also a very efficient operation, and not memory-wasteful, nor does it require all that much more compute time. In return, we get the safety and assurance of an immutable operation. Pretty good deal!

There are SO MANY MORE powerful iteration methods, all built in, like: filter, map, flatMap, findLast, and reduceRight. It gets even better when you throw in a functional library like Ramda or FP-TS, and add all the category theory stuff to it (maybe that will be the next post? Who wants to learn some category theory? :D )

So I hope this inspires you to never use loop statements again. Why would you need to?

Top comments (0)