-1

First off am a beginner practicing my JavaScript. My solution to this problem will be posted. I think its worth mentioning this took almost two days of pondering to solve The Problem: I am required to write an algorithm that will return the mode(s) from the given input array. For example:

mode([4, 5, 6, 6, 6, 7, 7, 9, 10]) ➞ [6]

mode([4, 5, 5, 6, 7, 8, 8, 9, 9]) ➞ [5, 8, 9]

mode([1, 2, 2, 3, 6, 6, 7, 9]) ➞ [2, 6]

Solution:

function mode(nums) {
  let array = [...nums]
  array = array.sort((a, b) => a - b) //sorts the array from lowest value

  // function to figure out the unique numbers and return as an array
  function uniqueNums(array) {
    let uniques = []
    for (let i = 0; i < array.length; i++) {
      if (!uniques.includes(array[i])) {
        uniques.push(array[i])
      }
    }
    return uniques
  }

  //function to return the mode of every unique number

  function counter(array) {
    let modes = []
    for (let i = 0; i < array.length; i++) {
      let count = 1, // keeps track of occurrence's of a number
        track = 1 //variable enables the while loop keep checking
      while (array[i] === array[i + track]) {
        count++
        track++
      }
      modes.push(count)
      i += count - 1
    }
    return modes
  }

  //function to return the highest mode(s)
  function highestMode(uniques, modes) {
    let highest = [],
      max = 0 //tracks our highest number in the array

    //loops to find highest mode
    for (let i = 0; i < modes.length; i++) {
      if (max < modes[i]) {
        max = modes[i]
      }
    }

    //loops to push position of modes equal to the highest mode
    for (let i = 0; i < modes.length; i++) {
      if (max === modes[i]) {
        highest.push(i)
      }
    }

    //uses the position of highest modes to swap them with their 
    //actual values
    let result = highest.map(a => a = uniques[a])
    return result
  }
  return highestMode(uniqueNums(array), counter(array))
}


console.log(mode([4, 4, 4, 6, 8, 9, 10, 10]))
  • 1
    If you [group the values](https://stackoverflow.com/questions/5667888/counting-the-occurrences-frequency-of-array-elements) you can then sort the groups by length and get the top one(s). – VLAZ Jan 20 '21 at 18:47

3 Answers3

2

If you're looking at this as a learning exercise, here's another implementation of the same algorithm from CertainPerformance, but written quite differently.

const mode = (
  ns, 
  counts = ns .reduce ((m, n) => m .set (n, (m .get (n) || 0) + 1), new Map ()),
  max = Math .max (... counts .values())
) => 
  [...counts] .flatMap (([n, c]) => c == max ? [n] : [])


console .log (mode ([4, 5, 6, 6, 6, 7, 7, 9, 10])) //=> [6]
console .log (mode ([4, 5, 5, 6, 7, 8, 8, 9, 9])) //=> [5, 8, 9]
console .log (mode ([1, 2, 2, 3, 6, 6, 7, 9])) //=> [2, 6]

If it's not clear, my counts matches to grouped and my max to maxCount.

The big difference from the answer by CertainPerformance is that this is written with pure expressions rather than statements. This is a style that I try to follow as much as I can do so practically.

It uses default parameters as a poor-man's substitute for let bindings available in other languages. It's not a perfect substitute and there is at least one potential problem with it, but it can be quite useful. Doing this let me define some helper variables that you can't assign in a function body without statements. There are alternatives, but I find this simplest when I can get away with it.

The reduce call is essentially the same as the other answer. So is the max definition.

But I take advantage of the fact that the default iterator of a Map is the list of entries, to turn the map into [[4, 1], [5, 1], [6, 3] ...] just by using the spread operator (...), without a need to call .entries().

Finally, I replace a call to filter and then to map with a single call to flatMap. This feels more elegant.

I'm not trying to suggest that this code is better than the one from CertainPerformance. It's quite similar in feel, although different in arrangement. But it is a different approach to the problem, and it might have something to offer because of that.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
1

I'd count up the number of occurrences of each element into an Map. Then use Math.max to find the largest value(s) in the map, and then take the keys which are equal to that largest value:

const mode = arr => {
  const grouped = new Map();
  for (const item of arr) {
    grouped.set(item, (grouped.get(item) || 0) + 1);
  }
  const maxCount = Math.max(...grouped.values());
  return [...grouped.entries()]
    .filter(([, count]) => count === maxCount)
    .map(([key]) => key);
};

console.log(mode([4, 5, 6, 6, 6, 7, 7, 9, 10])) // ➞ [6]
console.log(mode([4, 5, 5, 6, 7, 8, 8, 9, 9])) // ➞ [5, 8, 9]
console.log(mode([1, 2, 2, 3, 6, 6, 7, 9])) // ➞ [2, 6]
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Your solution makes use of concepts I haven't learnt before, exactly what i was hoping to get out of this post. Thanks – Frank Imade Jan 20 '21 at 19:03
  • 1
    The Map may make things slightly more confusing than is strictly needed for the algorithm. You could achieve the same thing using a plain object instead, except that objects can only have string (or symbol) keys - using a plain object and turning the array of keys into an array of numbers afterwards might make a bit more intuitive sense if you're trying to get a grasp on data structure manipulation. – CertainPerformance Jan 20 '21 at 19:04
  • If you don't mind I would love for you to explain your final return statement the syntax is hard for me to grasp. – Frank Imade Jan 20 '21 at 19:09
  • 1
    `[...grouped.entries()]` gives an array of entries, eg `[[4, 1], [5, 1], [6, 3] ...`: each subarray contains the key (one of the original numbers) and the number of occurrences of that key. Filter by those which have the highest occurrences and you're left with `[[6, 3]]`, then map each subarray to only the key and you get `[6]`. – CertainPerformance Jan 20 '21 at 19:12
  • Wow you're amazing, how did you get so good with handling data structures? also how would you rate this problem from a scale of 1 - 10? – Frank Imade Jan 20 '21 at 19:16
  • 1
    Practice and answering questions. You can find a whole lot of examples by looking at decent SO questions: https://stackoverflow.com/search?tab=newest&q=%5bjavascript%5d%20%5barrays%5d%20%5bobject%5d%20score%3a1%20hasaccepted%3ayes Rate the problem? I dunno, it's quite straightforward and doesn't really require much thought (IMO) – CertainPerformance Jan 20 '21 at 19:18
  • The `Map` is more useful than suggested. If you changed to an Object, then your keys would be strings. And if you converted them back to numbers at some point, the same code would no longer work for an array of strings. That would be unfortunate. – Scott Sauyet Jan 20 '21 at 21:46
1

A more simpler and optimized approach with single iteration

https://jsfiddle.net/dv7f9nxr/

function mode(items) {
  var result = [];
  var count = {};
  var highest= 0;

  items.map((item) => {
    var itemCount = (count[item] || 0) + 1;
    count[item] = itemCount;
      if(itemCount > highest) {
        highest = itemCount;
        //reset 
        result = [item];
      } else if (itemCount === highest){
        result.push(item);
    }

  })
 
  
  
  return result;
}

console.log(mode([2, 3, 9, 6, 9]))
console.log(mode([2, 3,2, 9, 6, 9]))
console.log(mode([2, 3,2, 9, 6, 9,2]))
console.log(mode([2, 3, 9, 6, 9,6,2]))
Wakeel
  • 4,272
  • 2
  • 13
  • 14