1

I have an array of objects and I want to create a sequence based on each value. For example take the array:

let starting_data = [
  {str: "a"},
  {str: "a"},
  {str: "b"},
  {str: "c"},
  {str: "a"},
  {str: "c"},
  {str: "a"},
  {str: "b"}
]

I know I can use a map and reduce to find the unique number of each str value

let uniques = starting_data.map(function(value) {return value.str;}).reduce((acc, val) => {
      acc[val] = acc[val] === undefined ? 1 : acc[val] += 1;
      return acc;
}, {})

But I'm not sure how to leverage this to eventually get what I want as my desired output:

let desired_output = [
  {str: "a", count: 1},
  {str: "a", count: 2},
  {str: "b", count: 1},
  {str: "c", count: 1},
  {str: "a", count: 3},
  {str: "c", count: 2},
  {str: "a", count: 4},
  {str: "b", count: 2}
]

Where the counts start over for each unique value, but the as go to 4, and b and c go to 2. I tried messing with arr.sort if I need to do that first but I wasn't sure how to sort based on a value inside an object... Any help appreciated!!

2 Answers 2

1

You mentioned it you want it to be purely functional, so here it is.

let starting_data = [
  { str: "a" },
  { str: "a" },
  { str: "b" },
  { str: "c" },
  { str: "a" },
  { str: "c" },
  { str: "a" },
  { str: "b" },
];

const uniqueCount = starting_data
  .reduce(
    (arr, { str }) => {
      const hash = arr[0];
      hash[str] ||= 0;
      arr.push({ str, count: ++hash[str] });
      return arr;
    },
    [{}]
  )
  .slice(1);

console.log(uniqueCount);

This prints:

[
  { str: 'a', count: 1 },
  { str: 'a', count: 2 },
  { str: 'b', count: 1 },
  { str: 'c', count: 1 },
  { str: 'a', count: 3 },
  { str: 'c', count: 2 },
  { str: 'a', count: 4 },
  { str: 'b', count: 2 }
]

How it works

This solution keeps a hash as the first element of the array being built by reduce.

  1. The array starts as [{}].
  2. Populate the hash key with a 0 if the key doesn't already exist.
  3. Push a new element to the array with str and a count.
  4. hash[str] is incremented by 1 and then used as the value.

The array now looks like:

[{ a: 1 }, { str: "a", count: 1 }]
  1. We continue this until we've iterated through every item.
[ { a: 2 }, { str: 'a', count: 1 }, { str: 'a', count: 2 } ]
[ { a: 2, b: 1 }, { str: 'a', count: 1 }, { str: 'a', count: 2 }, { str: 'b', count: 1 } ]
// Continues until it finishes
  1. At the end, we remove this initial hash by slicing the array onwards from the first element.

Hope this helps.

Sign up to request clarification or add additional context in comments.

Comments

1

can do something like keeping a seperate object to keep track of the counts

let starting_data = [
  {str: "a"},
  {str: "a"},
  {str: "b"},
  {str: "c"},
  {str: "a"},
  {str: "c"},
  {str: "a"},
  {str: "b"}
]


let counts = {};
let res = starting_data.map(({str}) => {
    if(!counts[str])counts[str]=0;
  counts[str]+=1
  return {str,count:counts[str]}
})

console.log(res)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.