Find array elements frequency, then return sorted associative array

Hi Campers!

So I have an array of strings, like the following letters = ['a', 'b', 'b', 'c', 'b', 'a', 'a', 'b', 'c', 'c', 'b']

My goal is to find the frequency of each element , and then return an associative array sorted in descending order based on the frequency (and if the frequency is the same, alphabetically), such as [{letter: "b", frequency: 5}, {letter: "a", frequency: 3}, {letter: "c", frequency: 3}]

Any Ideas on how to approach this?

Thanks!

2 Likes

Have you attempted to write any sort of algorithm? I would first start thinking how you would accomplish this if you were given a long list of letters such as and how you would keep track of them with pen and paper. Your algorithm will be very much like that process.

2 Likes

Solved. A bubble sort has done the trick.

So what was your final solution?

I did the following:

const letters = ['e','c','b', 'a', 'b', 'b', 'a', 'a', 'b', 'c', 'c', 'b'];

const letterFrequencies = letters
  .reduce((freqs, letter) => Object
    .assign(freqs, { [letter]: (freqs[letter] || 0 ) + 1 }), {});

const letterFrequencyArr = Object
  .keys(letterFrequencies)
  .map(letter => ({ letter, frequency: letterFrequencies[letter] }));

letterFrequencyArr.sort((a, b) => b.frequency - a.frequency || a.letter.localeCompare(b.letter));

console.log(letterFrequencyArr);
/* displays the following in the console
[
  { letter: 'b', frequency: 5 },
  { letter: 'a', frequency: 3 },
  { letter: 'c', frequency: 3 },
  { letter: 'e', frequency: 1 }
]
*/
1 Like

A very elegant solution, Thanks!
Here’s my mammoth function:

var letters = ['a', 'b', 'b', 'c', 'b', 'a', 'a', 'e', 'b', 'c', 'c', 'b'];

function countSortDescend(arr) {
  return reverseBubbleSortBy(count(arr), l => l.frequency);

  function reverseBubbleSortBy(array, cb) {
    for (let n = 1; n < array.length; n++) {
      for (let i = 0; i < array.length - n; i++) {
        if (cb(array[i]) < cb(array[i + 1])) swap(array, i, i + 1);
      }
    }
    return array;
  }

  function count(array) {
    var result = [],
      dups = array.length;
    for (let i = 0; i < dups; i++) {
      let frequency = 1;
      for (let ii = i + 1; ii < dups; ii++) {
        if (array[i] === array[ii]) {
          frequency++;
          swap(array, ii, dups - 1);
          ii--;
          dups--;
        }
      }
      result.push({
        value: array[i],
        frequency
      });
    }

    return result;
  }

  function swap(arr, i1, i2) {
    var temp = arr[i1];
    arr[i1] = arr[i2];
    arr[i2] = temp;
  }
}

console.log(countSortDescend(letters));

/* 

[ { value: 'b', frequency: 5 }, 
  { value: 'a', frequency: 3 }, 
  { value: 'c', frequency: 3 }, 
  { value: 'e', frequency: 1 } ] 

*/

Pass the following array to your function and you will see it does not actually do what you wanted.

var letters = ['e','c','b', 'a', 'b', 'b', 'a', 'a', 'b', 'c', 'c', 'b'];

Your solution returns:

[ { value: 'b', frequency: 5 },
  { value: 'c', frequency: 3 },
  { value: 'a', frequency: 3 },
  { value: 'e', frequency: 1 }
]

when it should return:

[ { value: 'b', frequency: 5 }, 
  { value: 'a', frequency: 3 }, 
  { value: 'c', frequency: 3 }, 
  { value: 'e', frequency: 1 }
] 

The ‘a’ should come before the ‘c’ based on the requirements you described originally.