0

I have an array with strings that I want to count:

const myArr = ["a, b, c", "a", "b", "a, b", "b", "a", "a", "a, b, c, d"]

I want to get a count for how many a, b, c, and d in myArr.

Seeing answers from this post, I could use array methods, map(), split(), flat() then reduce():

const res = myArr.map((x) => x.split(", ")).flat().reduce((acc, x) => {
    return acc[x] ? ++acc[x] : (acc[x] = 1), acc;
  }, {});

console.log(res)
// { a: 6, b: 5, c: 2, d: 1 }

Although the above code gives the desired output, I want to do everything within one run of reduce(). Is it possible?

I've tried something out, but still don't know how I could complete it:

const res2 = myArr.reduce((acc, x) => {
  acc.push(x.split(", "));
  return acc.flat();
}, []);

console.log(res2)
// [ 'a', 'b', 'c', 'a', 'b', 'a', 'b', 'b', 'a', 'a', 'a', 'b', 'c', 'd' ]
//                  ^^^^^^^^^^ obviously not what I want!
Emman
  • 3,695
  • 2
  • 20
  • 44
  • Does this answer your question? [Counting the occurrences / frequency of array elements](https://stackoverflow.com/questions/5667888/counting-the-occurrences-frequency-of-array-elements) – pilchard Aug 09 '22 at 08:00
  • 1
    You just have to move the operations done in your map into the reduce, but the efficiency is the same – pilchard Aug 09 '22 at 08:01
  • @pilchard, regarding your 1st comment -- I have indeed referenced to that post. Regarding your 2nd comment, I'm not sure what the right implementation within `reduce` is . My goal is to get the count using one call to `reduce`, regardless of what's going on inside the reducer function. – Emman Aug 09 '22 at 08:03

4 Answers4

1

You could iterate the values inside each split value from myArr to update the accumulator:

const myArr = ["a, b, c", "a", "b", "a, b", "b", "a", "a", "a, b, c, d"]

const res = myArr.reduce((acc, x) => {
  x.split(', ').forEach(v => acc[v] = (acc[v] || 0) + 1);
  return acc;
}, {})

console.log(res)
Nick
  • 138,499
  • 22
  • 57
  • 95
1

You can simply move the split into the reduce method.

const myArr = ["a, b, c", "a", "b", "a, b", "b", "a", "a", "a, b, c, d"];

const result = myArr.reduce((acc, str) => {
  const chars = str.split(', ');

  for(const x of chars) {
    acc[x] ??= 0;
    acc[x]++;
  }
  
  return acc;
}, {});

console.log(result);
Reyno
  • 6,119
  • 18
  • 27
1

Here is an one-line version:

const myArr = ["a, b, c", "a", "b", "a, b", "b", "a", "a", "a, b, c, d"]

const out = myArr.reduce((o, s) => (s.split(", ").forEach(i => o[i]++ || (o[i] = 1)), o), {});

console.log(out);
n--
  • 3,563
  • 4
  • 9
0

You can try this:

First you split it up by comma and do an flatMap.

After this, you map it again to trim the whitespace.

After this, you can start to count it:

const myArr = ["a, b, c", "a", "b", "a, b", "b", "a", "a", "a, b, c, d"]

let result = myArr.flatMap(c => c.split(","))
                  .map(c => c.trim())
                  .reduce((prev, curr) => {
                     if(prev[curr]) prev[curr]++
                     if(!prev[curr]) prev[curr] = 1;
                     return prev
                  }, {})

console.log(result)
bill.gates
  • 14,145
  • 3
  • 19
  • 47
  • Thanks for the answer. However, I'm explicitly looking to avoid operations external to `reduce`. In fact, that is the point of the posted question. – Emman Aug 09 '22 at 09:00