2

I know I could use variations of this answer to find out how many different types of data there are in an array:

How to count the number of certain element in an array?

But, what I am looking for is whether there is a simple way to count how many different types of data there are in an array:

I have arrays which may have 0, 1, or 2 as values

so it could be:

a = [1,2,0,1,1,1];

or they may all be the same and a different length:

a = [1,1,1,1,1,1,1,1,1,1,1,1,1];

In javascript, I would like a function that returns "1" if all values are the same, "2" if there is a mixture of just two of the possible values, and if the array contains all three values, "3".

Any help appreciated. Thanks.

Community
  • 1
  • 1
Russell
  • 655
  • 2
  • 9
  • 21

2 Answers2

3

The simple approach is to keep a map of found values, and remember every time you add to it: Live Example | Live Source

function countUniques(a) {
    var valuesSeen = {};
    var count = 0;

    a.forEach(function(value) {
        if (!valuesSeen[value]) {
            ++count;
            valuesSeen[value] = true;
        }
    });
    return count;
}

(Note: That uses ES5's forEach. If you're using an old browser, you'll need to shim that, search for "ES5 shim" to find a shim for it.)

Or if you don't like the people you work with very much and love using operators instead of branching statements: Live Copy | Live Source

function countUniques(a) {
    var valuesSeen = {}, count = 0;

    a.forEach(function(value) {
        valuesSeen[value] || (++count,  valuesSeen[value] = true);
    });
    return count;
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
3

Just for fun, here's a "funkier" (and somewhat obfuscated) solution using .reduce that requires no local variables:

function countUniques(a) {
     return a.reduce(function(p, v) {
         p.c += !(v in p.s); p.s[v] = 1; return p;
     }, {c:0, s:{}}).c;
}

It's functionally identical to TJC's answer, except that the valuesSeen and count values are passed around as an object p as the "previous" value passed from the prior iteration of .reduce. The p.c element is equivalent to TJC's count and p.s is valuesSeen.

Note that .reduce (like .forEach) is an ES5 function which will require a shim on older browsers.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • OMG that gets an upvote for entertainment value alone! And I thought my alternate using `||` and `,` was for "if you don't like the people you work with very much". No no no, *yours* is. :-) – T.J. Crowder Jun 29 '13 at 17:18
  • 1
    @T.J.Crowder functional programming courses 20+ years ago FTW ;-) – Alnitak Jun 29 '13 at 17:20
  • Just for fun another elusive way: `return p.c += p.s[v] = !(v in p.s), p;`. – Felix Kling Jun 29 '13 at 19:52
  • @FelixKling really nice! :) I was looking for a way to (ab)use the comma operator but hadn't found it yet. It did take me a while to figure out how yours works given that unlike the original (and TJC's code) `p.s[v]` doesn't remain constant, until I realised that (of course) the `v in p.s` test doesn't care what the value is, only that the key exists. – Alnitak Jun 29 '13 at 20:14