2

I need to convert an array like the following:

["Cats", "Cats", "Dogs", "Dogs", "Dogs"]

the the following form:

[
    { x: "Cats", y: 35 },
    { x: "Dogs", y: 40 },
    { x: "Birds", y: 55 },
]

where y represents the number of the same elements in the array.

0

3 Answers 3

3

Highlighting use of the efficient, modern Map-based approach that can also be found in similar forms in the duplicate:

const getCounts = iterable => {
    const counts = new Map();

    for (const x of iterable) {
        counts.set(x, (counts.get(x) ?? 0) + 1);  // use || for ES6 compat
    }

    return counts;
};

Array.from(
    getCounts(["Cats", "Cats", "Dogs", "Dogs", "Dogs"]),
    ([x, y]) => ({x, y})
)
Sign up to request clarification or add additional context in comments.

Comments

1

Here's a faster version using object lookup:

const arr = ["Cats", "Cats", "Dogs", "Dogs", "Dogs", "Birds"];
const result = Object.entries(arr.reduce((acc, cur) => {
  acc[cur] = (acc[cur] || 0) + 1;
  return acc;
}, Object.create(null))).map(([x, y]) => ({x, y}));

console.log(result);

Edit

As Ry-♦ mentioned, there was a potential bug that the string "__proto__" etc. can't be count correctly. So changed from {} to Object.create(null) to avoid it.

1 Comment

This can have conflicts with Object.prototype properties. Better to use a Map if supported (which also allows for types other than strings), or Object.create(null) in a pinch.
0

Another approach would be to use Array#reduce() to efficiently build a dictionary of { x : Animal, y : Count } objects, and then extract the required array of values from the dictionary via Object.values():

const input = ["Cats", "Cats", "Dogs", "Dogs", "Dogs"]

/* Obtain array of values from reduced dictionary */
const result = Object.values(input.reduce((dict, value) => {
  
  /* Fetch, or insert starting item for value in dictionary */
  const item = dict[value] || { x : value, y : 0 }
  
  /* Increment y counter for item */
  item.y++;

  /* Update dictionary value with item */  
  dict[value] = item;

  return dict

}, {}));

console.log(result);

One of the advantages with this approach is that it avoids the overhead of an extra call to .map(), making it more efficent.

5 Comments

or const result = Object.entries(input.reduce((a, v) => (a[v]=(a[v]||0)+1, a), {})) .map(([x, y]) => ({x, y}));
@JaromandaX yep that'd do it. It's nice to avoid an extra map(), when possible :)
@JaromandaX Lol almost exactly the same answer, didn't even notice
If worried about efficiency, copying the object every iteration for O(n^2) time should be avoided.
@Ry- point taken , updated answer

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.