DEV Community

loading...
Cover image for Code Smell 53 - Explicit Iteration

Code Smell 53 - Explicit Iteration

mcsee profile image Maxi Contieri ・1 min read

We learned loops back in school. But enumerators and iterators are the next generation.

Problems

  • Encapsulation

  • Declarativeness

Solutions

  1. Favor foreach() or high order iterators

  2. You will be able to use yield(), caches, proxies, lazy loading and much more when you hide your implementation details.

Sample Code

Wrong

Right

Detection

Linters can find this smell using regex.

There might be false positives. See exceptions below.

Exceptions

If the problem domain needs the elements to be bijected to natural numbers like indices the first solution is adequate.

Remember all time to find real world analogies.

Tags

  • Declarative

Conclusion

This kind of smell do not ring the bell to many developers because they think this is a subtlety.

Clean code is full of this few declarative things that can make a difference.

Relations

More info

Credits

Photo by Elena Mozhvilo on Unsplash


If you get tired of writing for loops, take a break and continue later.

David Walker


Original twitter thread

Discussion (15)

pic
Editor guide
Collapse
shadowtime2000 profile image
shadowtime2000

The Right JS code is kind of wrong syntax wise and stuff. This would be the correct code:


for (color in colors) {
  print(colors[color]);
}

//Closures and arrow functions
colors.foreach(color => console.log(color);
Enter fullscreen mode Exit fullscreen mode
Collapse
mouseeatscat profile image
Michel Descoteaux

Or in this case it could have been simplified even further to:

for (color of colors) {
  console.log(color);
}

//Closures and arrow functions
colors.forEach(color => console.log(color));
Enter fullscreen mode Exit fullscreen mode
Collapse
ifarmgolems profile image
Patrik Jajcay
colors.forEach(console.log);
Enter fullscreen mode Exit fullscreen mode
Thread Thread
mcsee profile image
Collapse
bugrahasbek profile image
Buğra Hasbek

I don't use js and this confused the hell out of me at first sight :) i was expecting color to be an element in colors but it is used as an index? I think I prefer the c++ way

for (auto color:colors) 
  print(color);
Enter fullscreen mode Exit fullscreen mode
Collapse
patarapolw profile image
Pacharapol Withayasakpunt
  • Depends on the programming language; but JavaScript in is damn confusing.
  • A closing bracket is missing at the end.
Collapse
qm3ster profile image
Mihail Malo
  1. Yeah, unless they wanted to print keys (including inherited), they should have used of not in.
  2. a closing parenthesis*
Thread Thread
mcsee profile image
Maxi Contieri Author

corrected! thank you!

Collapse
patarapolw profile image
Pacharapol Withayasakpunt

I also prefer to hide explicit iteration. Still, I had to use the first style for chunking.

const batchSize = 500

for (let i = 0; i < items.length; i += batchSize) {
  await batchInsert(items.slice(i, i + batchSize))
}
Enter fullscreen mode Exit fullscreen mode

The second style (for ... of) is for awaiting inside, or for await ... of.

The third style, I generally use map because of performance, but grammatically, it would be for each. Also, map(async () => ...) can be used with Promise.all (or Promise.allSettled).

Collapse
mcsee profile image
Maxi Contieri Author

In my opinion chinking should be hidden at iteration level.

Collapse
patarapolw profile image
Pacharapol Withayasakpunt

That is, if a convenient library function exists.

Collapse
qm3ster profile image
Mihail Malo
  1. You want to use of with iterators, in is for string keys of objects (including inherited).
  2. for in and for of syntax in js looks like for (const ident of expression) (where const could be let or var and of could be in)
  3. there's a missing ) in the last statement
  4. most commonly available print fn in js is console.log
  5. Array method is called forEach (capitalization)

so, all in all:

const colors = ['red', 'green']
const {log} = console

for (const color of colors)
  log(color)

// closure/arrow function
colors.forEach(color => log(color))
Enter fullscreen mode Exit fullscreen mode

if for some reason you have to use in to iterate over values of an object, it's typically done like this (to avoid iterating over inherited keys)

for (const key in obj)
  if (obj.hasOwnProperty(key))
    do_thing(key, dict[key])
Enter fullscreen mode Exit fullscreen mode

however you should prefer the new iterators, such as Object.keys(), Object.values() and Object.entries().
As such, the above becomes:

for (const [key, val] of Object.entries(obj))
    do_thing(key, val)
Enter fullscreen mode Exit fullscreen mode

Finally, if you see yourself operating on keys of an Object generically like this, you might instead be looking for Map, which naturally iterates by "entries", and supports non-string keys.

const ref = {two: 2}
const m = new Map([[1, "val1"], [ref, "val2"]])
for (const [k,v] of m)
  console.log("key", k, "value", v)
Enter fullscreen mode Exit fullscreen mode

See also Set, WeakMap, WeakSet

Collapse
mcsee profile image
Maxi Contieri Author

Wow!

How many detaill!

I will make your corrections

Nevertheless i use many different languages on all my code smells and i like to think them as pseudo code without entering any language details. Your solution is clearly a better one but it is too tied to a particular language.

Collapse
qm3ster profile image
Mihail Malo

What other languages do you use?

Thread Thread
mcsee profile image
Maxi Contieri Author

see my code smells

up to now:
javascript
pyhton
php
golang
java
ruby

Language is accidental
Code smells are not related to any particular language

I write code snippets as examples but I think them most as pseudocode