ES6 reduce elements of an array into an array of the frequency of occurence of its elements

Viewed 278

I have an array that contains comma separated numbers in a string, a sample array looks like this

const arr = ["2", "1,2", "2,3", "1,3"]

I want to create another array which contains the sum of the frequency of occurrence of these values.

Here is the algo I am trying to implement

  1. loop through each element, via a reducer that has an accumulator array
  2. split each element and convert them into an array of numbers
  3. if the accumulator index is empty, initialize it to zero, otherwise add 1, to pre-existing value

Here is the code, I am trying

arr.reduce((acc, item) => { 
item.split(",").map((value, index) => {
    val = parseInt(value) 
    acc[val] === null ? acc[val] = 0 : acc[val] += 1 
  })
})

I get an error Uncaught TypeError: Cannot read property 2 of undefined

4 Answers

You have to provide a default value for the accumulator also I suggest that you use .forEach() instead of using .map(), map() function used to generate a new array, here you just want to loop through the array, one more thing, you have to return accumulator at every iteration.

const arr = ["2", "1,2", "2,3", "1,3"];

let newArr = arr.reduce((acc, item) => {
  item.split(",").forEach((value, index) => {
    val = parseInt(value);
    acc[val] === undefined ? (acc[val] = 0) : (acc[val] += 1);
  });
  return acc;
}, {});

console.log(newArr);

Although you can use map, that's for building a new Array from an existing array. And although you can use a reducer, that's meant for incrementally calculating an accumulator. Since what you're doing instead is just looping over split values you don't need to use map, and since you're mutating an end result instead of calculating it, you may want to consider using more performant looping mechanisms that do less of the work that you don't need:

const acc = {};
for (const item of arr) {
  for (const value of item.split(",")) {
    val = parseInt(value) 
    acc[val] === null ? acc[val] = 0 : acc[val] += 1 
  }
}

I see a couple of issues.

  1. you do not provide an initial accumulator.
  2. acc[val] will be undefined not null the first time.
  3. you need to return the accumulator for the next iteration

so

const arr = ["2", "1,2", "2,3", "1,3"]

const results = arr.reduce((acc, item) => {
  item.split(",").map((value, index) => {
    val = parseInt(value)
    acc[val] === undefined ? acc[val] = 0 : acc[val] += 1
  })
  return acc;
}, {})

console.log(results);

const arr = ["2", "1,2", "2,3", "1,3"]
var count = {};
arr.reduce((acc, item) => { 
    item.split(",").map((value, index) => {

       if(value in acc) acc[value]++;
       else acc[value] = 1

     })

     return acc;
},count);

Result is an object like this {1: 2, 2: 3, 3: 2}

Related