58

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.

3
  • Copying and sorting sounds like a good solution. Why would you want to avoid that? Commented Feb 24, 2013 at 14:32
  • 2
    You can push the elements in a dictionary and count the number of keys. Commented Feb 24, 2013 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. Commented Feb 24, 2013 at 14:36

9 Answers 9

117

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).

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

1 Comment

I am blown away, I never used sets before. Thank you for your explination! this was extremly helpful.
70

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.

8 Comments

This does only work if the unique elements are strings (or convert to unique strings)
@Bergi granted, but they are in OP's example.
If you mean jam, beef, cream, and jam, they are variables in the OPs example. We don't know what they contain
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.
counts[arr[i]] may not always be set. || 0 ensures we get 0 if it isn't.
|
21

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).

1 Comment

or acc[val] === undefined ? acc[val] = 1 : acc[val] += 1; :)
10

Same as this solution, but less code.

let counts = {};
arr.forEach(el => counts[el] = 1  + (counts[el] || 0))

Comments

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.

3 Comments

Both filter and [last]indexOf require JavaScript 1.6. This answer could use some explanatory comments.
The filter callback should return a boolean, not the element. And your for-each loop does not the same as the OPs original script
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.
1

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);

Comments

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]);  

2 Comments

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.
Built method will always have its own cons
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

Comments

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;
}

1 Comment

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, 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.

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.