10 JavaScript forEach Hacks You Need to Know

Javascript

Introduction

JavaScript forEach loop is a fundamental tool in a developer’s toolkit, enabling efficient iteration through arrays. However, to truly harness its potential, understanding its nuances and learning how to use it effectively is essential. In this blog post, we’ll delve into ten powerful strategies to maximize the utility of the Javascript forEach loop. These techniques will not only optimize your code but also enhance its readability and maintainability. Let’s unlock the full potential of forEach and elevate your JavaScript programming skills.

1. Leverage Arrow Functions on Javascript forEach

JavaScript arrow functions are a shorter and more concise way to write functions in JavaScript. They use the => syntax, which looks like an arrow. They’re handy for writing one-liner functions or simplifying code. Instead of using the traditional function keyword and curly braces, arrow functions allow you to express functions in a more compact and modern style.

Without arrow functions:

const fruits = ["apple", "banana", "orange"];

function printFruit(fruit) {
  console.log(fruit);
}

fruits.forEach(printFruit);

With arrow functions:

const fruits = ["apple", "banana", "orange"];

fruits.forEach(fruit => console.log(fruit));

There is an absolute masterpiece article here on the differences between arrow functions and regular functions that you can check it out for more details.

2. Handle the this Context on Javascript forEach

Understanding the usage of this in JavaScript can indeed be challenging.

In JavaScript, this essentially represents the current context or object in which a function is running. It acts as a placeholder indicating the function’s execution context. The actual value of this is determined by how the function is invoked, and it can vary based on different scenarios.

So, when you call a function, this is all about where and how you called it. It’s like a method actor – it adapts to the role it’s playing.

When working in JavaScript, an object can not only hold data but also functions as properties. Take this example: we have an object called obj with properties such as value, number, and printNumbers. Interestingly, printNumbers is a method that can be accessed using obj.printNumbers().

This method essentially multiplies each number in an array by the value specified. However, to specify the context in which the printNumbers method should execute, we use .bind(this) at the end of the forEach call. This allows us to determine that the method printNumbers() should execute within the context of obj in this case.

const obj = {
  value: 10,
  numbers: [1, 2, 3],
  printNumbers() {
    this.numbers.forEach(function(num) {
      console.log(num * this.value);
    }.bind(this));
  }
};

obj.printNumbers();
// 10
// 20
// 30

Basically, if you attempt to use printNumbers() outside of obj, it won’t work properly. Why? Because it can’t find the value and numbers it needs. It’s like trying to bake a cake without flour and eggs – recipe’s messed up!

Without .bind(this)

const obj = {
  value: 10,
  numbers: [1, 2, 3],
  printNumbers() {
    this.numbers.forEach(function(num) {
      console.log(num * this.value);
    };
  }
};

obj.printNumbers();
// NaN
// NaN
// NaN

3. Early Exit with some or every on Javascript forEach

Think of Array.prototype.some() as a way to check if at least one item in an array meets a specific condition.

Imagine you have a bunch of numbers and you want to know if at least one of these numbers is an even number (like 2, 4, 6) or an odd number (like 1, 3, 5).

In JavaScript terms, using Array.prototype.some() is like going through each card and checking if the number on it is odd. If you find at least one even number, some() happily says, “Yes, there’s an odd number!” If you don’t find any even numbers and they’re all even, it says, “Sorry, no odd numbers”.

Using some() is like having a swift way to check if there’s at least one item in the array that fits what you’re looking for, like finding an even number in a list of numbers.

This becomes incredibly handy when combined with forEach() because you might not want to run a specific chunk of code for all elements in the array. With some() alongside forEach(), you can quickly spot what you need and exit early, saving unnecessary processing for the rest of the elements.

const mixNumbers = [3, 4, 9, 13, 15];

// Checks if some element is even, if yes it will not execute
mixNumbers.forEach(num => {
  if (mixNumbers.some((element) => element % 2 === 0)) return false;
  console.log(num);
});
// It will not print any number


const oddNumbers = [3, 5, 9, 13, 15];

// If all elements are odd it will execute normally
oddNumbers.forEach(num => {
  if (oddNumbers.some((element) => element % 2 === 0)) return false;
  console.log(num);
});
// 3
// 5
// 9
// 13
// 15

This approach also pairs well with the every() method. However, it will halt execution only if every single element meets the condition (like being odd). On the other hand, some() will stop execution the moment it finds an element that fits the condition (like being even). So, every() is like the “check all boxes” approach, while some() is the “stop at the first one that fits” approach.

const mixNumbers = [3, 4, 9, 13, 15];

// Checks if some element is even, if yes it will not execute
mixNumbers.forEach(num => {
  if (mixNumbers.every((element) => element % 2 === 0)) return false;
  console.log(num);
});
// 3
// 4
// 9
// 13
// 15

4. Utilise Array Destructuring with Javascript forEach

JavaScript array destructuring is a way to unpack values from an array into separate variables. It’s like taking things out of a box and giving them names so you can use them easily.

We talked about object destructuring here

Imagine you have a box filled with items (an array). Array destructuring allows you to take out each item and put them into their own labeled containers (variables). So, if you have an array [a, b, c], array destructuring lets you do something like const [x, y, z] = [a, b, c] so that x contains a, y contains b, and z contains c.

A very simple example would be:

const colors = ['red', 'blue', 'green'];

const [first, second, third] = colors;

console.log(first);  // Output: 'red'
console.log(second); // Output: 'blue'
console.log(third);  // Output: 'green'

This makes it simple to work with arrays, especially when you only need specific parts of them for your code.

Imagine you have a list of individuals in an array. Each person is not just a name; they also have a profession. Your task is to display both the person’s name and their profession. Here’s where array destructuring comes into play: it allows you to easily access and use these details as you iterate through the array.

const people = [
  { name: 'John Doe', profession: 'Engineer' },
  { name: 'Jane Smith', profession: 'Teacher' },
  // more people...
];

people.forEach(({ name, profession }) => {
  console.log(`${name} is a ${profession}.`);
});
// "John Doe is a Engineer."
// "Jane Smith is a Teacher."

In this case, the forEach loop is like going through each person, and the destructuring part { name, profession } is like pulling off their name and job tags.

5. Handle Asynchronous Operations with Javascript forEach

Imagine you’re making a sandwich in your kitchen. Now, you realize you’re out of mayonnaise. You need to quickly run to the store to get some.

  • Synchronous Action: In a synchronous world, you would pause making the sandwich, go to the store, buy mayonnaise, come back, and then resume making the sandwich. You wait until one task is fully complete before moving on to the next.
  • Asynchronous Action: Now, in an asynchronous world, you’d ask your friend to get the mayonnaise while you continue making the sandwich. Your friend will bring it when they can, and you can keep working on the sandwich in the meantime. You don’t stop everything; you do tasks simultaneously.

In programming terms, when we talk about asynchronous operations, it’s like your computer multitasking. It can start a task, like loading a webpage or fetching data, and in the meantime, it can do other things. When the webpage is ready or the data is fetched, it’ll handle that. It’s all about efficiency and not waiting for one thing to finish before moving on to the next.

Okay, for reasons beyond the topic of this post use async/await with Javascript forEach in its not the ideal neither the most readable solution, you can see more the details on this great post. But we can use the for..of approach which is much more readable.

Async/Await Approach:

  • Using async/await for API calls is like making requests to fetch data from different endpoints and patiently waiting for each response to arrive in the order they were requested.
  • It’s akin to saying, “Fetch data from these endpoints in order, and let me know when each response is ready. I’ll process them as they arrive.”
async function fetchDataFromEndpoints(endpoints) {
  for await (const endpoint of endpoints) {
    const data = await fetchData(endpoint);
    console.log(`Data fetched from ${endpoint}:`, data);
    processData(data);
  }
}

const endpoints = ['api/endpoint1', 'api/endpoint2', 'api/endpoint3'];

fetchDataFromEndpoints(endpoints);

Promises Approach:

  • Promises for API calls are like making requests to fetch data from different endpoints without a specific order, trusting that they’ll arrive, and you’ll process them as they do.
  • It’s similar to saying, “Fetch data from these endpoints, and let me know whenever the responses are ready. I’ll process them as they come, no specific order.”
function fetchDataFromEndpoints() {
  const endpoints = ['api/endpoint1', 'api/endpoint2', 'api/endpoint3'];

  endpoints.forEach(endpoint => {
    fetchData(endpoint)
      .then(data => {
        console.log(`Data fetched from ${endpoint}:`, data);
        processData(data);
      })
      .catch(error => {
        console.error(`Error fetching data from ${endpoint}:`, error);
      });
  });
}

// Data fetched from api/endpoint3, { street: 1234 Elm St, city: Faketown, state: FS, postalCode: 12345, country: Fakeland}
// Data fetched from api/endpoint1, { user: John Doe }
// Data fetched from api/endpoint2, { cart: { product: smartphone, price: 250 } }

In both scenarios, whether using async/await or Promises, we’re making asynchronous API calls (data retrieval) within a loop. Async/await ensures the responses are processed in the requested order, simulating an organized sequence of data handling. Conversely, Promises handle responses without a specified order, reflecting a more adaptable, response-based approach to data processing from various API endpoints.

6. Combine Javascript forEach with map for Transformation

Using forEach here might seem a bit extra, but it’s cool to know that map and forEach aren’t exactly the same. The examples we’ve shown are pretty straightforward. However, if your loop gets fancier, using forEach could be a smoother ride instead of trying to shoehorn it into map.

Let’s check the difference between the two methods:

  • Javascript forEach: It’s like going through a list and doing something with each item one by one. You’re just taking a look at each item.
  • Javascript map: It’s also like going through a list, but while doing something with each item, map() creates a new array with the results of transforming each item in the original array. So, you’re not only looking at each item, but you’re also creating a new list based on what you did with each item.

So depending on your use case, one option will sound more appealing than the other.

Using map():

const numbers = [1, 2, 3];

let doubledNumbers = [];
doubledNumbers = numbers.map(num => {
  return num * 2;
});

console.log(doubledNumbers); //[2, 4, 6]

using forEach():

const numbers = [1, 2, 3];

const doubledNumbers = [];
numbers.forEach(num => {
  doubledNumbers.push(num * 2);
});

console.log(doubledNumbers); //[2, 4, 6]

7. Use Set for Unique Values Javascript forEach

In JavaScript, a Set is a built-in data structure that allows you to store a collection of unique values, whether they are primitive types or object references. The key characteristic of a Set is that it will only hold unique elements, meaning no duplicates are allowed.

So I believe we both can see a very clear use for this here, right? Perhaps one of the easiest ways to remove duplicates from your array.

const numbers = [1, 2, 1, 3, 2];

const uniqueNumbers = new Set();
numbers.forEach(num => {
  uniqueNumbers.add(num);
});

console.log([...uniqueNumbers]); // [1, 2, 3]

Alright, that example was a bit too straightforward. Let’s spice things up a bit. Picture this: you’ve got a collection of IDs, and these IDs could represent anything—books, products, images, you name it. Your job is to figure out if an item is already in your collection. If it’s there, give the user a heads-up, but if it’s not, toss it into the collection.

const userIDs = new Set();

// Function to register users
function registerUsers(userIDsToAdd) {
  userIDsToAdd.forEach(userID => {
    if (userIDs.has(userID)) {
      console.log(`User ID ${userID} already taken. Please choose another.`);
    } else {
      userIDs.add(userID);
      console.log(`User ID ${userID} registered successfully!`);
    }
  });
}

const newUsers = [123, 456, 123, 789]; // Including a duplicate ID (123)

registerUsers(newUsers);

// User ID 123 registered successfully!
// User ID 456 registered successfully!
// User ID 123 already taken. Please choose another.
// User ID 789 registered successfully!

8. Combine with filter for Selection

Just like the example with forEach along with map, this might seem a little elaborate too. But it ultimately depends on how your application was constructed and how you’re utilizing these loops in your code.

But to clarify the differences, in simple words, forEach lets you perform an action for each item without altering the original set of items. On the other hand, filter assists in crafting a fresh set with only the items that satisfy particular criteria.

So, once again, the choice between these approaches depends entirely on how your application was designed and the way you intend to use these functionalities.

Using filter:

const numbers = [1, 2, 3, 4, 5];

let evenNumbers = [];
evenNumbers = numbers.filter(num => {
  return num % 2 === 0
});

console.log(evenNumbers); // [2, 4]

Using forEach:

const numbers = [1, 2, 3, 4, 5];

const evenNumbers = [];
numbers.forEach(num => {
  if (num % 2 === 0) {
    evenNumbers.push(num);
  }
});

console.log(evenNumbers); // [2, 4]

9. Error Handling within the forEach loop

Here’s another intriguing example. Picture having an array filled with a diverse range of elements, and the catch is, you’re not entirely in charge of what’s in that array, nor can you easily sift through and pick what you want. The challenge here is identifying and flagging elements that don’t match the expected types, perhaps throwing an error to say, “Hey, this one’s not the right type!” In this particular case, forEach comes to the rescue, offering a way to tackle this interesting challenge. Let me walk you through how this can be done!

const numbers = [1, 2, 3, 'four', 5];

numbers.forEach(num => {
  try {
    if (typeof num !== 'number') {
      throw new Error('Invalid number');
    }
    console.log(num);
  } catch (error) {
    console.error(error.message);
  }
});

// 1
// 1
// 1
// Invalid number
// 1

10. Parallel Execution with Promise.all

Imagine you have a bunch of tasks to do, like watering plants, cooking pasta, and charging your phone. Normally, you’d do one task at a time, waiting for one to finish before starting the next. That’s how your to-do list usually works.

Now, with Promise.all and parallel execution, it’s like having three helpers (like magic clones!) to do these tasks simultaneously. You tell each helper a task, and they go off to do them all at the same time. You don’t have to wait for one to finish before starting the next; they just let you know when they’re done. It’s like being super efficient and multitasking without being overwhelmed!

const asyncOperations = async () => {
  const endpoints = ['api/endpoint1', 'api/endpoint2', 'api/endpoint3'];
  console.log('Start of asyncOperations');
  
  const promises = [];
  
  endpoints.forEach(item => {
    promises.push(asyncFunction(item));
  });

  await Promise.all(promises);
  console.log('All promises have settled');
};

asyncOperations();

If you’re wondering the differences between the approach we presented on example 5, the main difference lies in how the asynchronous operations are handled and how they affect the overall execution flow:

  1. asyncOperations using async/await and Promise.all: In the asyncOperations function, items are processed concurrently. The function uses forEach to iterate over the items array, initiating asynchronous operations (asyncFunction) for each item and collecting the promises in an array. Then, it uses Promise.all to wait for all promises in the array to settle. This allows for concurrent execution of async operations.
  2. fetchDataFromEndpoints using Promises and forEach:In fetchDataFromEndpoints, the requests are initiated sequentially. It uses forEach to iterate over an array of endpoints, and for each endpoint, it initiates an asynchronous operation (fetchData) which returns a Promise. It then chains .then() to handle successful responses and .catch() to handle errors. However, each request is made one after the other, so they execute in a sequential manner.

In summary, the main difference is in how the asynchronous operations are handled. The first approach allows for concurrent execution of asynchronous operations using Promise.all, while the second approach executes the asynchronous operations sequentially within a forEach loop. The choice between them depends on whether you want concurrent or sequential execution based on your specific use case and requirements.

Conclusion

JavaScript’s forEach loop is more than just a simple array iteration mechanism—it’s a versatile tool that, when used effectively, can significantly enhance your code. By embracing techniques like leveraging arrow functions, managing the this context, and combining it with other array methods, you can write cleaner, more concise, and error-resilient code. Remember, mastering the forEach loop empowers you to write efficient, expressive JavaScript, ultimately improving your development workflow and the overall quality of your applications. Happy coding!🚀👨‍💻👩‍💻

Leave a Reply

Your email address will not be published. Required fields are marked *

0 Comments