DEV Community

Washing your code: avoid loops

Artem Sapegin on May 16, 2019

You’re reading an excerpt of my upcoming book on clean code, “Washing your code: write once, read seven times.” Preorder it on Leanpub or read a ...
Collapse
 
itsjzt profile image
Saurabh Sharma • Edited

while I don't find for of loops less readable than any functional array transformation.

I use functional methods heavily for array transformations or when I use React.

Other than that I find loops more readable when logic is longer than 3, 4 line or contains many if/else.

In async stuff loops are much better because in map/reduce you need to await the map/reduce and make the inner function async and also await inside it.

for ex:

async function getUsers (userIds) {
  const users = userIds.map(async userId => {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  });
  // ... do some stuff
  return users;
}
Collapse
 
samuraiseoul profile image
Sophie The Lionhart

The reduce in your example is bad because it is not a reduce, its should be a map, reduce is for reducing the entirety to one value normally to sum or average or something.

if (!props.item && props.item.details) { return; }
props.item.details.clients.map(function(client) {
    return client.errorConfigurations.map(function(config){
        usedIn : client.client.name,
        errorMessage : config.error.message,
        errorLevel: config.error.level
    });
}).flat(); // I did not test this at all, I take no responsibility for anything

Also using the function(){} syntax is still useful if you give the function a name, helps give it some extra semantics over the use of arrow functions.

Collapse
 
qm3ster profile image
Mihail Malo
const tableData =
  props.item &&
  props.item.details &&
  props.item.details.clients.reduce((acc, client) =>
    acc.concat(
      ...client.errorConfigurations.map(config => ({
        errorMessage: config.error.message,
        errorLevel: config.error.level,
        usedIn: client.client.name
      }))
    )
  );

is missing the initial value for reduce, such as a [].
And like @samuraiseoul mentioned, this is a case for a flatMap.

const tableData = props.item?.details?.clients.flatMap(
  ({ client, errorConfigurations }) =>
    errorConfigurations.map(({ error }) => ({
      errorMessage: error.message,
      errorLevel: error.level,
      usedIn: client.name
    }))
)

If you don't have your own flatMap yet, store bought is fine:

const flatMap = <T, R>(fn: (x: T) => R[], arr: T[]) => {
  const out: T[] = []
  const { push } = Array.prototype
  for (const x of arr) push.apply(out, fn(x))
}

JK, don't use this one, it's probably not very good.

Collapse
 
sapegin profile image
Artem Sapegin

Thanks for pointing the missing initializer, fixing!

Collapse
 
qm3ster profile image
Mihail Malo

Object#fromEntries() to the rescue:

const allNames = {
  hobbits: ['Bilbo', 'Frodo'],
  dwarfs: ['Fili', 'Kili']
};
const kebabNames = Object.fromEntries(
  Object.entries(allNames).map(([race, names]) => [
    race,
    names.map(name => name.toLowerCase())
  ])
)