How to return a value from a forEach function (ES6)

Hi everyone,

So I am trying to understand what I am doing wrong with my function. It’s just a silly function I’ve thrown together for learning purposes.

index.html

<ul><li id="el" class="Chris Ted John">Element</li></ul>

app.js

import a from './modules/test';

function init() {

  const el = document.getElementById('el');

  const test2 = el.classList.forEach((item) => {
    console.log(a(item));
    return a(item);
  });
  console.log(test2);

}
init();

test.js

const obj = {
  Chris: 'uncool',
  John: 'uncool',
  Ted: 'uncool'
};

const a = (item) => {
  obj[`${item}`] = `${item} is cool`;
  return obj;
};

export default a ;

I would like to be able to return the value for obj during the last iteration of the loop. and have it available inside of the scope of the init() function. However it seems to return undefined. How do I do this?

When you want to take in an array and return a single value, you are REDUCING that array to a single value, which means you need array.prototype.reduce. According to MDN, forEach returns undefined. Basically, all the effects in your callback (console logging) are side effects of processing the array (they aren’t creating a new array and passing it back as a result of the array function, nor are they reducing the array and passing that value back as a result of the array function). Array.prototype.forEach is for when you ONLY want side effects. Reduce is the most general case of array processing in a function (for loops are the most flexible). You want reduce().

Awesome thanks for the tip. However I’m now getting an error saying classlist.reduce is not a function?

app.js

import a from './modules/test';

function init() {

  const el = document.getElementById('el');

  const test2 = el.classList.reduce((item) => {
    // console.log(a(item));
    return a(item);
  });
  console.log(test2);

}
init();

Ok, slight update…

app.js

import a from './modules/test';

function init() {

  const el = document.getElementById('el');

  const classArray = [...el.classList];

  const test2 = classArray.reduce((item) => {
    // console.log(a(item));
    return a(item);
  });

  console.log(test2);

}
init();

I got the reduce method working by converting the classlist DOM into an actual array.

But my only problem now is that reduce is only returning the first iteration of the array. I would like the last. Is there an easy way to do this?

Okay, it took some research to figure out what was going on. element.classList isn’t an array. It’s a DOMTokenList, which is a set of space-separated tokens indexed by zero if you call classList.item(index). You need some code to turn it into an array.

const classes = [];
el.classList.forEach( item => classes.push(item));
const test2 = classes.reduce ...

Thanks. I found a similar solution through googling.

Your reduce callback is the problem. The callback takes the form:
(accumulator, value, index, arr) => { //commands}, and will use arr[0] and arr[1] as the first values of accumulator and value unless you pass in an initialValue for accumulator as the second parameter to reduce, in which case arr[0] is the first value

Honestly, I’m surprised (but happy in my concision-loving heart) that the Reddit solution works. I thought because it was a string that spreading it would lead to a character array. But classList.values returns an Iterator, so maybe it’s implicitly happening.

Yeah, the ES6 solution is pretty concise!

So I’ve had a quick go at implementing the reduce function and sadly I’m still running into trouble…

app.js

import a from './modules/test';

function init() {

  const el = document.getElementById('el');

  const classArray = [...el.classList];

  const test2 = classArray.reduce((_acc, item) => {
    console.log(a(item));
    return a(item);
  });

  console.log(test2);

}
init();

This seems to skip the first value in the array and work for the next two? Not sure why.

  const test2 = classArray.reduce((_acc, item) => {
    console.log(a(item));
    return a(item);
  }, null);
1 Like

Excellent! Thanks a million man!

I even noticed you can reduce this to one line. :grin:

Here’s my final solution

app.js

import a from './modules/test';

function init() {

  const el = document.getElementById('el');

  const classArray = [...el.classList];

  const test2 = classArray.reduce((_acc, item) => a(item), null);

  console.log(test2);

}
init();
1 Like

True, but I thought you wanted to log the intermediate values for the calls to a(item).