简体   繁体   中英

Scala equivalent of Haskell's insertWith for Maps

I'm looking to do the simple task of counting words in a String. The easiest way I've found is to use a Map to keep track of word frequencies. Previously with Haskell, I used its Map 's function insertWith , which takes a function that resolves key collisions, along with the key and value pair. I can't find anything similar in Scala's library though; only an add function ( + ), which presumably overwrites the previous value when re-inserting a key. For my purposes though, instead of overwriting the previous value, I want to add 1 to it to increase its count.

Obviously I could write a function to check if a key already exists, fetch its value, add 1 to it, and re-insert it, but it seems odd that a function like this isn't included. Am I missing something? What would be the Scala way of doing this?

Use a map with default value and then update with +=

import scala.collection.mutable

val count = mutable.Map[String, Int]().withDefaultValue(0)
count("abc") += 1
println(count("abc"))

If it's a string then why not use the split module

import Data.List.Split

let mywords = "he is a good good boy"
length $ nub $ splitOn " " mywords
5

If you want to stick with Scala's immutable style, you could create your own class with immutable semantics:

class CountMap protected(val counts: Map[String, Int]){
    def +(str: String) = new CountMap(counts + (str -> (counts(str) + 1)))
    def apply(str: String) = counts(str)
}

object CountMap {
    def apply(counts: Map[String, Int] = Map[String, Int]()) = new CountMap(counts.withDefaultValue(0))
}

And then you can use it:

val added = CountMap() + "hello" + "hello" + "world" + "foo" + "bar"
added("hello")
>>2
added("qux")
>>0

You might also add apply overloads on the companion object so that you can directly input a sequence of words, or even a sentence:

object CountMap {
    def apply(counts: Map[String, Int] = Map[String, Int]()): CountMap = new CountMap(counts.withDefaultValue(0))
    def apply(words: Seq[String]): CountMap = CountMap(words.groupBy(w => w).map { case(word, group) => word -> group.length })
    def apply(sentence: String): CountMap = CountMap(sentence.split(" "))
}

And then the you can even more easily:

CountMap(Seq("hello", "hello", "world", "world", "foo", "bar"))

Or:

CountMap("hello hello world world foo bar")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM