I'm trying to take an array (for an example, an array of years) and then make a new array of sub-arrays which tells, firstly, the unique element in the original array and, secondly, how many time it was repeated.

For example, lets say I start of with an array of numbers [1999, 1999, 2000, 2005, 2005, 2005, 2015]

I would like my function to return an array like [[1999, 2], [2000, 1], [2005, 3], [2015, 1] ]
because the year 1999 was repeated twice, the year 2000 was was not repeated, the year 2005 was repeated three times, etc.

I can successfully make a new array that removes the duplicates, but Im getting some weird behavior when it comes to making my sub-arrays.

EDIT

Here was my own faulty solution, in case anyone wants to point out what i did wrong.

var populateYearsList = function(yearsDuplicate){

var uniqueYears = [];
for (var i=0; i < yearsDuplicate.length; i++){
 if(uniqueYears.indexOf(yearsDuplicate[i]) == -1){
  uniqueYears.push(yearsDuplicate[i])
} else {
  console.log("duplicate found") };
}
 console.log (uniqueYears)
};

I ran into the problem of trying to change uniqueYears.push(yearsDuplicate[i]) into uniqueYears.push([ yearsDuplicate[i], 1 ]) and then trying to replace my console.log into some sort of incremented counter.

4

There are 4 answers

4
TaoPR On BEST ANSWER

It is as easy as:

var input = [1999, 1999, 2000, 2005, 2005, 2005, 2015];

var uniq = [];
input.forEach(function(n){
    if (uniq.indexOf(n)<0) 
        uniq.push(n);
});

var output = uniq.map(function(n){
    return [n, input.filter(function(m){ return m == n }).length]
});

Quick explanation how it works

Given the input array, map its unique version to the new array with this mapping:

element ---> [ element, the number of occurrences in the original array ]

EDIT NOTE:: FIXED the previous solution which might introduced duplicate element.

1
Stefan Dimov On

This is how I would do it:

var input = [1999, 1999, 2000, 2005, 2005, 2005, 2015];

function numberOfOccurancesArray (input) {
    var result = {};

    for (var i = 0; i < input.length; i++) {

        var currentYear = input[i];

        // if there is an element with the name of the currentYear
        if (result[currentYear]) {

            // increace its prop `count` with 1
            result[currentYear].count += 1;

        } else {

            // if not present, create it
            result[currentYear] = {
                year: currentYear,
                count: 1
            }
        }
    }

    return result;
}

Here is sample code : JsFiddle

2
Wojciech Grzebieniowski On
var years = [1999, 1999, 2000, 2005, 2005, 2005, 2015];

var occurences = [];

for(var i = 0; i < years.length; i++ ) {
    var year = years[i];
    if(typeof occurences[year] === 'undefined') {
        occurences[year] = 1;
    } else {
        occurences[year]++;   
    }
}

occurences = occurences.map(function(occurences, year) {
    return [year, occurences];
});

console.log(occurences);

EDIT: Much faster solution

    var results = [];
    var uniq = [];
    var counts = [];
    var i = 0;

    for (i; i < years.length; i++) {
        var year = years[i];
        var indexOf = uniq.indexOf(year);
        if (indexOf < 0) {
            uniq.push(year);
            counts[uniq.length - 1] = 1;
        } else {
            counts[indexOf] += 1;
        }
    }

    i = 0;

    for (i; i < uniq.length; i++) {
        results.push([uniq[i], counts[i]]);
    }

    return results;
1
NG. On

If you want to play with functional paradigms, reduce it to a dictionary and then map the keys to a tuple.

var yearToCounts = input.reduce(function(counts, year) {
    if (year in counts) {
        counts[year]++;
    } else {
        counts[year] = 1;
    }
    return counts;
}, {});

return Object.keys(yearToCounts).map(function(year) {
    return [year, yearToCounts[year]];
});