49

In JavaScript the following will find the number of elements in the array. Assuming there to be a minimum of one element in the array

arr = ["jam", "beef", "cream", "jam"]
arr.sort();
var count = 1;
var results = "";
for (var i = 0; i < arr.length; i++)
{
    if (arr[i] == arr[i+1])
    {
      count +=1;
    }
    else
    {
        results += arr[i] + " --> " + count + " times\n" ;
        count=1;
    }
}

Is it possible to do this without using sort() or without mutating the array in any way? I would imagine that the array would have to be re-created and then sort could be done on the newly created array, but I want to know what's the best way without sorting. And yes, I'm an artist, not a programmer, your honour.

Ghoul Fool
  • 6,249
  • 10
  • 67
  • 125
  • Copying and sorting sounds like a good solution. Why would you want to avoid that? – Bergi Feb 24 '13 at 14:32
  • 2
    You can push the elements in a dictionary and count the number of keys. – zs2020 Feb 24 '13 at 14:34
  • I just want to know a way around this. I'm just curious. Secondly, it messes up the original array, which other elements rely on. I might be better off turning the array into an object. But I don't know what would happen to the various parts of an object on a sort. – Ghoul Fool Feb 24 '13 at 14:36

9 Answers9

106

The fast way to do this is with a new Set() object.

Sets are awesome and we should use them more often. They are fast, and supported by Chrome, Firefox, Microsoft Edge, and node.js.
What is faster Set or Object? by Andrei Kashcha

The items in a Set will always be unique, as it only keeps one copy of each value you put in. Here's a function that uses this property:

function countUnique(iterable) {
  return new Set(iterable).size;
}

console.log(countUnique('banana')); //=> 3
console.log(countUnique([5,6,5,6])); //=> 2
console.log(countUnique([window, document, window])); //=> 2

This can be used to count the items in any iterable (including an Array, String, TypedArray, and arguments object).

Web_Designer
  • 72,308
  • 93
  • 206
  • 262
  • I am blown away, I never used sets before. Thank you for your explination! this was extremly helpful. – ahmad Oct 13 '21 at 15:33
69

A quick way to do this is to copy the unique elements into an Object.

var counts = {};
for (var i = 0; i < arr.length; i++) {
    counts[arr[i]] = 1 + (counts[arr[i]] || 0);
}

When this loop is complete the counts object will have the count of each distinct element of the array.

kojiro
  • 74,557
  • 19
  • 143
  • 201
  • This does only work if the unique elements are strings (or convert to unique strings) – Bergi Feb 24 '13 at 15:04
  • 3
    @Bergi granted, but they are in OP's example. – kojiro Feb 24 '13 at 15:08
  • 1
    If you mean `jam`, `beef`, `cream`, and `jam`, they are variables in the OPs example. We don't know what they contain – Bergi Feb 24 '13 at 16:02
  • Ah, true, I assumed they were just mistakenly unquoted, like in so many other SO questions. – kojiro Feb 24 '13 at 16:03
  • 4
    This is the greatest piece of code ever. It did exactly what I wanted it to do with far fewer lines than what I was trying. BUT...how does this work? I can't figure out why this works the way it does. – Chelsea Aug 21 '17 at 01:50
  • This is a very elegant solution! However, I don't understand entirely the idea inside the for block. We create new key-value pairs with `counts[arr[i]] = 1`, but what does `(counts[arr[i]] || 0 )` mean? – dex10 Nov 09 '20 at 06:22
  • 2
    `counts[arr[i]]` may not always be set. `|| 0` ensures we get 0 if it isn't. – kojiro Nov 09 '20 at 12:37
  • it only counts one item – Mohammad Ayoub Khan May 09 '21 at 19:33
20

Why not something like:

var arr = ["jam", "beef", "cream", "jam"]
var uniqs = arr.reduce((acc, val) => {
  acc[val] = acc[val] === undefined ? 1 : acc[val] += 1;
  return acc;
}, {});
console.log(uniqs)

Pure Javascript, runs in O(n). Doesn't consume much space either unless your number of unique values equals number of elements (all the elements are unique).

Ash Catchem
  • 911
  • 11
  • 13
10

Same as this solution, but less code.

let counts = {};
arr.forEach(el => counts[el] = 1  + (counts[el] || 0))
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
kryptoatom
  • 180
  • 1
  • 8
8

This expression gives you all the unique elements in the array without mutating it:

arr.filter(function(v,i) { return i==arr.lastIndexOf(v); })

You can chain it with this expression to build your string of results without sorting:

.forEach(function(v) {
     results+=v+" --> " + arr.filter(function(w){return w==v;}).length + " times\n";
});

In the first case the filter takes only includes the last of each specific element; in the second case the filter includes all the elements of that type, and .length gives the count.

Plynx
  • 11,341
  • 3
  • 32
  • 33
  • Both `filter` and `[last]indexOf` require JavaScript 1.6. This answer could use some explanatory comments. – kojiro Feb 24 '13 at 14:36
  • 1
    The filter callback should return a boolean, not the element. And your for-each loop does not the same as the OPs original script – Bergi Feb 24 '13 at 15:02
  • This will work but I downvoted because it isn't order of n. The Set and object property approaches given in other answers will perform significantly better with large arrays. – Gary Jul 01 '18 at 22:21
2

This answer is for Beginners. Try this method you can solve this problem easily. You can find a full lesson for reduce, filter, map functions from This link.

const user = [1, 2, 2, 4, 8, 3, 3, 6, 5, 4, 8, 8];

const output = user.reduce(function (acc, curr) {
    if (acc[curr]) {
        acc[curr] = ++acc[curr];
    } else {
        acc[curr] = 1;
    }
    return acc;
}, {});

console.log(output);
Hishan_98
  • 194
  • 1
  • 2
  • 12
0

function reomveDuplicates(array){
        var newarray = array.filter( (value, key)=>{
            return array.indexOf(value) == key
        });
        console.log("newarray", newarray);
    }
reomveDuplicates([1,2,5,2,1,8]);  

Using hash Map with the time complexity O(n)

function reomveDuplicates(array){

    var obj ={};
    let res=[];

    for( arg of array){
        obj[arg] = true;
    }

    console.log(Object.keys(obj));


    for(key in obj){
        res.push(Number(key));  // Only if you want in Number 
    }

    console.log(res);
}
reomveDuplicates([1,2,5,2,1,8]);  
  • When this question was written, eight years ago, the latter would have been a reasonable solution. (The former is `O (n^2)` and I wouldn't use it except for fairly small arrays.) But today, this is a trivial one-liner: `const removeDuplicates = (array) => [...new Set (array)]`. I no longer see a reason for either technique. – Scott Sauyet Feb 22 '21 at 15:32
  • Built method will always have its own cons – Ganesh Shetty Feb 22 '21 at 16:55
0

In a modern, extensible and easy-to-read approach, here's one using iter-ops library:

import {pipe, distinct, count} from 'iter-ops';

const arr = ['jam', 'beef', 'cream', 'jam'];

const count = pipe(arr, distinct(), count()).first;

console.log(count); //=> 3
vitaly-t
  • 24,279
  • 15
  • 116
  • 138
0
function check(arr) {
    var count = 0;

 for (var ele of arr) {
   
    if (typeof arr[ele] !== typeof (arr[ele+1])) {
      count++;
    } else {
      ("I don't know");
    }
  }
  return count;
}
Markitos
  • 13
  • 1
  • 4
    While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please [include an explanation for your code](//meta.stackexchange.com/q/114762/269535), as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Luca Kiebel Jan 08 '22 at 10:49