The iterator pattern is a design pattern which can be used to decouple algorithms from containers.
Let's take a look into what it actually is, how to apply it, and which problems it solves!
🧵👇
1⃣ What problems does it solve?
✅ The elements of an aggregate object should be accessed and traversed without exposing its representation (underlying data structure)
✅ New traversal operations should be defined for an aggregate object without changing its interface
Those two points may sound a little complicated, but you can view it this way:
If you define access and traversal operations within an object's interface (its methods viewable and usable from the outside), it becomes inflexible.
You simply can't change the interface without breaking some code, especially code which implements the interface!
This applies to all code. The one you've written, and the one others have written, third-parties so to say.
But if you provide a common interface, which all containers and users can agree on, you can decouple access and traversal logic completely, and let your container only return one common interface!
🧵👇
2⃣ An example problem
Imagine a collection of friends.
The class Friends abstracts away the underlying storage of friends and uses an array for that.
getFriends(index) already exposes some implementation details, and thus basically ties the container...
...to a certain data structure (an array in this case).
Because what happens if someone wants to loop over all friends?
The implementation could look something like the following.
But what happens if you want to refactor Friends to use a Set?
A Set is an unordered collection. It usually doesn't provide index-based access, and it doesn't necessarily maintain the order of insertion, as the backing implementation may vary!
There you have a problem!
👇
3⃣ Solving the issue
By using an iterator, you can decouple the access and traversal logic of a container from its underlying data structure.
See that interesting looking new method?
That's a JavaScript-specific, a Symbol (a well-known one) which the runtime will call on certain occasions.
One such an occasion is the use of the for..of loop.
You can change the underlying data structure, ...
... you can then change the implementation of the iterator, but every user can still loop over all the elements in the same way!
Most languages provide something like an iterator, be it an object behind a Symbol, an interface, or something similar.
👇
4⃣ Bonus: Some more interesting use cases of iterators in JavaScript
1. Traversing an array in reverse order without reversing the array itself, and thus not modifying it
It may sound tempting to simply reverse an array, but this is an in-place operation...
...and it can be a costly one if your array is pretty large.
With an iterator, you don't have those issues, because the iterator object itself handles everything, as it only keeps track of the index to return the next element from.
The original array can stay as it is!
2. Creating a range to iterate over, on-demand
No need to create an array and fill it with values, which all need memory, if you can simply generate those numbers on demand!
• • •
Missing some Tweet in this thread? You can try to
force a refresh
Although I am not a data scientist by any means, I was recently asked what knowledge of math someone would need to be able to efficiently get into the field.
Well, I researched a little and came up with the following course track, starting with the basics.
🧵👇
Calculus 1 will cover a lot of the basics, relevant especially to optimization problems which are quite common in data science.
The Dependency Inversion Principle is a part of SOLID, a mnemonic acronym which bundles a total of 5 design principles.
It is often associated with clean code.
But what exactly is it, is it important to you, should you even care?
🧵👇
1⃣ What does it state?
It states:
Modules that encapsulate high-level policy should not depend upon modules that implement details. Rather, both kinds of modules should depend upon abstractions.
This may sound a little complicated, but you can break it up, as follows:
1. High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces).
2. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
Clean code is important, clean code helps others understand your code, but clean code is also pretty subjective!
I want to give you my perspective on it, drawing from years of experience leading teams of developers as a tech lead and working in teams.
🧵
1️⃣ Clean code is important
Clean code helps people understand code.
The more your code is structured according to the opinion of the majority of developers, the more likely it is that other developers will understand your code!
A few principles of clean code are:
- Use meaningful and understandable identifiers
- Use functions and keep them as short as possible
- You don’t need comments most of the time when your code is human readable
- Don’t repeat yourself (DRY)
- Keep it Simple, Stupid! (KISS)