cela
cela

Reputation: 2500

Kotlin: Find most common element in collection

I am writing a function that iterates through a collection and finds the most common items occurring in it.

Here is what I have so far to get the values and add the number of times they appear in the collection. I'm putting the value into a map as a key and the number of times it appears as its value.

fun mostCommon(input: Collection<Int>): Set<Int> {
    var newMap: MutableMap<Int, Int> = HashMap()
    for (item in input) {
        if (newMap.containsKey(item)) {
            //TODO: add 1 to value if key is found
        } else {
            newMap.put(item, 1)
        }
    }
    return emptySet()
}

I am having trouble finding a way to add 1 to its value if the key already exists.

I tried doing this:

newMap[item] +=1

But I get an error about plusAssign(1) not being allowed on Nullable receiver.

Upvotes: 15

Views: 9734

Answers (5)

Muham
Muham

Reputation: 1

Answering @groff07 questions @groff07 questions

if u have two inputs with the same quantity, don't use :

numbersByElement.maxBy { it.value }?.key

beacause it will give the first key that gain max quantity

instead, u can use your own filtering, example i want tp print 2 which is the min e.g: {2=5,3=5}

val maxValue = numbersByElement.values.max()
var minkey = 5 //if it is guaranteed that max key is 1, 2, 3, 4, 5
for (map in numbersByElement) {
    if (map.value == maxValue) {
        if (map.key < minkey) {
            minkey = map.key
        }
    }
}

pirntln(minKey)

more explanation: techiedelight

Upvotes: 0

Minki
Minki

Reputation: 994

elegant nullsafe solution with Kotlin-1.4 preview

@Test
fun testFindOutMostCommonStringInList() {
    val mostCommon = findMostCommonInList(listOf("Foo", "Boo", null, "Foo", "Hello"))
    assertEquals("Foo", mostCommon)
}

@Test
fun testFindOutMostCommonIntInList() {
    val mostCommon = findMostCommonInList(listOf(1, 2, null, 3, 4, 5, 1))
    assertEquals(1, mostCommon)
}

private fun <T>findMostCommonInList(list : List<T>) : T? {
    return list
        .groupBy { it }
        .maxByOrNull { it.value.size }
        ?.key
}

Upvotes: 3

Ishan Bhatt
Ishan Bhatt

Reputation: 10259

I think the following should work.

val arr = listOf(1,2,3,4,5,6,7,3,4,2,3,1,4,5,2,7,8,2,3,3,3,3)
val maxOccurring = arr.groupBy { it }.mapValues { it.value.size }.maxBy { it.value }?.key

Returns 3 in this case.

Upvotes: 1

Kirill Rakhman
Kirill Rakhman

Reputation: 43841

Map.get returns an Int?, i.e. if no item is found, null will be returned. You can utilize the elvis operator to deal with that:

val newMap = mutableMapOf<Int, Int>()

for (item in input) {
    newMap[item] = (newMap[item] ?: 0) + 1
}

Another alternative is to use Java 8's Map.merge:

newMap.merge(item, 1) { i, j -> i + j }
// or the equivalent
newMap.merge(item, 1, Int::plus)

This will put 1 in the map, if the key is not present yet, and otherwise apply the lambda to the old and new value, in our case add the old value to the new value and store the result under the key.

Upvotes: 7

s1m0nw1
s1m0nw1

Reputation: 81997

As you already noticed, the error was related to nullability-handling. I'd suggest a more functional approach without explicit looping but simple grouping:

val numbersByElement = input.groupingBy { it }.eachCount()
//gives something like this {1=3, 2=5, 3=4, 5=2, 4=1}

The result is a Map with the elements from input as its keys and the number of occurrences of the elements as the corresponding values.

You can now find the most common element with maxBy:

numbersByElement.maxBy { it.value }?.key // gives an Int?

Upvotes: 33

Related Questions