3

Hi I've an array like this

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

my goal is to count the unique value and have a report about any items on it so the result's well be

Array [
   [5, 3],
   [2, 5],
   [9, 1],
   [4, 1]
]

I found a solution, that can be found in this other post [Counting the occurrences of JavaScript array elements

The @Emissary solution's for me is the best, the problem is that this solution go over and add some new function that I don't need and I can't reply directly to that post to ask how to have only the array that I need :D

@Emissary add the

console.log(key + ': ' + val)

My first idea was, instead console.log I can push every value in 2d array, but I don't think is a good idea, because if I have understand well the @Emissary solutions, the very 1st part of the solution create exactly the array that I need.

Any idea how to "isolate" this particular array?

7 Answers 7

5

This is an ideal situation to use a Map because it identifies by key, but also turns into that type of array when cast to it:

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

var result = [...a.reduce( (m, v) => m.set(v, (m.get(v) || 0) + 1), new Map() )];

console.log(result);

Note that solutions which perform a.filter on every iteration, have O(n²) time complexity, while this is O(n). Instead of the spread operator you can use Array.from(a.reduce(....)).

For older browsers you can use this variant:

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

var obj = a.reduce( function (m, v) { return m[v] = (m[v] || 0) + 1, m }, {} ),
    result = [];
for (var key in obj) {
    result.push([+key, obj[key]]);
}
console.log(result);

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

12 Comments

Using reduce is the best solution in terms of performances.
Tnx, seems to be quick, I only noticed that uses the spread syntax and I use an old browser Iceweasel and I don't know if can works. For now, tested only on node in windows, don't works for the spread sintax
Which support do you need? ES3, ES5? Do you have support for Map?
So you think the spread operation is O(1)... In my experience [...thingy] is a terribly slow operation.
Tnx, I don't know exactly on which browser this will run, for now I'm testing the code locally on node. Your second solution works on node, but don't return a 2d array, only a normal array. I think I'll implement both solution with a try code. The goal is to have a very fast way, because the data is huge.
|
3

You can do this with forEach() loop.

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

var result = []
a.forEach(function(e) {
  if (!this[e]) {
    this[e] = [e, 0];
    result.push(this[e])
  }
  this[e][1] ++
}, {})

console.log(result)

Comments

2

You can do it using Mapmap of ES6 and Convert it back into array using Array.from array.from

Map is similar like hash,which maintains a key value pair. Instead of traversing each element and maintaining a count,create a map and set the count of each occurrence and convert it to an array which is easy with es6

check this snippet

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var myMap2 = new Map();
a.forEach(function(num) {
  if (myMap2.has(num)) {
    var count = myMap2.get(num);
    myMap2.set(num, count + 1);
  } else
    myMap2.set(num, 1);
});

var arr = Array.from(myMap2);
console.log(arr);

Hope it helps

1 Comment

Tnx, this solution don't use the spread syntax and in my case is better because I need to grantee the best compatibility at all
1

Just pull it out of the Map constructor

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var aCount = [...new Set(a)].map(
  x => [x, a.filter(y => y === x).length]
);
console.log(aCount);
   

Comments

0

You could use the spread syntax with the solution of Emissary to get the an array with the wanted result.

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];

var aCount = [... new Map([... new Set(a)].map(
    x => [x, a.filter(y => y === x).length]
))];
              
console.log(aCount);

Comments

0

It's interesting to note that the reduce and spread syntax solution is also very efficient when evaluating array of words occurrences/frequency in a text (typically bag of words) when combined with the map to transform the output columns:

var r,words="We don't talk anymore\nWe don't talk anymore\nWe don't talk anymore\nLike we used to do\nWe don't love anymore\nWhat was all of it for?\nOh, we don't talk anymore\nLike we used to do\n\nI just heard you found the one you've been looking\nYou've been looking for"
words=words.split(/['\s]+/g);
r=[...words.reduce( (m, v) => m.set(v, ((m.get(v) || 0) + 1)), new Map() )].map(e=>[e[0],e[1]/words.length])
console.log(r)

Comments

0

Good solutions here but just for variety you might as well do as follows while getting your results sorted

var a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4],
thing = a.reduce((p,c) => (p[c] ? p[c]++ : p[c] = 1,p),[])
         .reduce((p,c,i) => c ? (p.push([i,c]),p) : p ,[]);
console.log(thing);

Ok... and one might complain about the possibility of generating a huge sparse array depending on the original numbers but hey no worries... The for in loop takes care of that. So lets do it again...

var  a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4],
sparse = a.reduce((p,c) => (p[c] ? p[c]++ : p[c] = 1,p),[]),
 dense = [];
for (var key in sparse) dense.push([+key,sparse[key]]);
console.log(dense);

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.