whatf
whatf

Reputation: 6458

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

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

Upvotes: 2

Views: 376

Answers (4)

ROOT
ROOT

Reputation: 11622

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);

Upvotes: 3

Gabriele Petrioli
Gabriele Petrioli

Reputation: 196177

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);

Upvotes: 2

Jacob
Jacob

Reputation: 78890

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 
  }
}

Upvotes: 3

HW Siew
HW Siew

Reputation: 1003

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}

Upvotes: -2

Related Questions