6

What is a more elegant way of writing a simple word count function in Swift?

//Returns a dictionary of words and frequency they occur in the string
func wordCount(s: String) -> Dictionary<String, Int> {
    var words = s.componentsSeparatedByString(" ")
    var wordDictionary = Dictionary<String, Int>()
    for word in words {
        if wordDictionary[word] == nil {
            wordDictionary[word] = 1
        } else {
            wordDictionary.updateValue(wordDictionary[word]! + 1, forKey: word)
        }
    }
    return wordDictionary
}

wordCount("foo foo foo bar")
// Returns => ["foo": 3, "bar": 1]
Maria Zverina
  • 10,863
  • 3
  • 44
  • 61
mmla
  • 1,578
  • 19
  • 21
  • 5
    I intend to leave this question as is. The Swift community wins when we can learn about language features by reading alternative implementations. 'Elegance' is taking advantage of syntactic sugar, readability, simplicity and even performance. Also, there is plenty of precedent for allowing subjective questions that evoke lots of different answers. See [here](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background) as an example. – mmla Jun 09 '14 at 07:47

4 Answers4

11

Your method was pretty solid, but this makes a couple improvements. I store the value count using Swifts "if let" keyword to check for an optional value. Then I can use count when updating the dictionary. I used the shorthand notation for updateValue (dict[key] = val). I also split the original string on all whitespace instead of just a single space.

func wordCount(s: String) -> Dictionary<String, Int> {
    var words = s.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
    var wordDictionary = Dictionary<String, Int>()
    for word in words {
        if let count = wordDictionary[word] {
            wordDictionary[word] = count + 1
        } else {
            wordDictionary[word] = 1
        }
    }
    return wordDictionary
}
Dash
  • 17,188
  • 6
  • 48
  • 49
3

I don't think this is more elegant because the readability is terrible and it requires and extension on Dictionary but it was really fun to write and shows you the potential power of swift:

extension Dictionary {
    func merge(with other: [KeyType:ValueType], by merge: (ValueType, ValueType) -> ValueType) -> [KeyType:ValueType] {
        var returnDict = self
        for (key, value) in other {
            var newValue = returnDict[key] ? merge(returnDict[key]!, value) : value
            returnDict.updateValue(newValue, forKey: key)
        }
        return returnDict
    }
}

func wordCount(s: String) -> [String:Int] {
    return s.componentsSeparatedByString(" ").map {
        [$0: 1]
    }.reduce([:]) {
        $0.merge(with: $1, +)
    }
}
wordCount("foo foo foo bar")

I do think that merge extension would be useful in other circumstances though

drewag
  • 93,393
  • 28
  • 139
  • 128
1

Couldn't find any traces of a Counter type class in the Collections library. You can improve the code slightly by using optional chaining.

func wordCount(s: String) -> Dictionary<String, Int> {
    var words = s.componentsSeparatedByString(" ")
    var wordDictionary = Dictionary<String, Int>()
    for word in words {
        if wordDictionary[word]? {
            wordDictionary[word] = wordDictionary[word]! + 1
        } else {
            wordDictionary[word] = 1
        }
    }
    return wordDictionary
}

wordCount("foo foo foo bar")
Maria Zverina
  • 10,863
  • 3
  • 44
  • 61
0

maybe not elegant but was fun to implement

let inString = "foo foo foo bar"

func wordCount(ss: String) -> Dictionary<String, Int> {
    var dict = Dictionary<String, Int>()
    var tempString = "";var s = ss + " "
    for each in s{
        if each == " "{ if dict[tempString] {
                var tempNumber = Int(dict[tempString]!)
                dict[tempString] = Int(tempNumber+1)
                tempString = ""
            } else {dict[tempString] = 1;tempString = ""}
        } else {
            tempString += each}}
    return dict
}

println(wordCount(inString))
Chéyo
  • 9,107
  • 9
  • 27
  • 44