arraysswiftsorting

Getting the most frequent value of an array


I have an Array of numbers and I want to know which number is most frequent in this array. The array sometimes has 5-6 integers, sometimes it has 10-12, sometimes even more - also the integers in the array can be different. So I need a function which can work with different lengths and values of an array.

One example:

myArray = [0, 0, 0, 1, 1]

Another example:

myArray = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]

Now I am searching for a function which gives out 0 (in the first example) as Integer, as it is 3 times in this array and the other integer in the array (1) is only 2 times in the array. Or for the second example it would be 4.

It seems pretty simple, but I cannot find a solution for this. Found some examples in the web, where the solution is to work with dictionaries or where the solution is simple - but I cannot use it with Swift 3 it seems...

However, I did not find a solution which works for me. Someone has an idea how to get the most frequent integer in an array of integers?


Solution

  • let myArray = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]
    
    // Create dictionary to map value to count   
    var counts = [Int: Int]()
    
    // Count the values with using forEach    
    myArray.forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
    
    // Find the most frequent value and its count with max(by:)    
    if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
        print("\(value) occurs \(count) times")
    }
    

    Output:

    4 occurs 4 times
    

    Here it is as a function:

    func mostFrequent(array: [Int]) -> (value: Int, count: Int)? {
        var counts = [Int: Int]()
    
        array.forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
    
        if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
            return (value, count)
        }
    
        // array was empty
        return nil
    }
    
    if let result = mostFrequent(array: [1, 3, 2, 1, 1, 4, 5]) {
        print("\(result.value) occurs \(result.count) times")    
    }
    
    1 occurs 3 times
    

    Update for Swift 4:

    Swift 4 introduces reduce(into:_:) and default values for array look ups which enable you to generate the frequencies in one efficient line. And we might as well make it generic and have it work for any type that is Hashable:

    func mostFrequent<T: Hashable>(array: [T]) -> (value: T, count: Int)? {
    
        let counts = array.reduce(into: [:]) { $0[$1, default: 0] += 1 }
    
        if let (value, count) = counts.max(by: { $0.1 < $1.1 }) {
            return (value, count)
        }
    
        // array was empty
        return nil
    }
    
    if let result = mostFrequent(array: ["a", "b", "a", "c", "a", "b"]) {
        print("\(result.value) occurs \(result.count) times")
    }
    
    a occurs 3 times