How to create a function that compares a string to an array of strings in a dictionary and assigns points based on key?

Viewed 61

Here is the problem: Given a word, compute the scrabble score for that word.

I've created a dictionary to track the characters and their correlated Scrabble points. I made a function that iterates over each character in the dictionary array and if it contains the iterated character it adds a point. Unfortunately, the function doesn't tally up characters that are repeated and I can't seem to understand why...

// struct contains the Scrabble Points dictionary

struct Scrabble {
     static let scrabblePoints = [1  : ["AEIOULNRST"],
                           2  : ["DG"],
                           3  : ["BCMP"],
                           4  : ["FHVWY"],
                           5  : ["K"],
                           8  : ["JX"],
                           10 : ["QZ"]]
}


var points = 0

// I can't figure out why my function doesn't iterate over repeated characters...


func pointGenerator(word:String) -> Int {
    for (key, letters) in Scrabble.scrabblePoints {
        for i in letters {
            for j in i {
                 if word.contains(j) {
                    points += key
                }
            }
        }
    }
    return points
}
//score is inaccurate.
4 Answers

I think it helps if you have a lookup from letter to points

extension String {
    var scrabbleScore: Int {
        let letterPoints: [Character:Int] = [
            "A":1,
         "B":3,
         "C":3,
         "D":2,
         "E":1
        ]
        return self.uppercased().reduce(into: 0) { sum, letter in
            sum += letterPoints[letter] ?? 0
        }
    }
}

"BEAD".scrabbleScore // 7

You could also write it like so, which avoids the hashing:

extension String {
    var scrabbleScore: Int {
        reduce(into: 0) { sum, letter in
            sum += letter.scrabbleValue
        }
    }
}

extension Character {
    var scrabbleValue: Int {
        switch self {
        case "A", "E", "I", "a", "e", "i": return 1
        case "B", "C", "b", "c": return 3
        case "D", "d": return 2
        default: return 0
        }
    }
}

To answer your question: you only add in the points for a given letter once, no matter how many times the word contains the letter:

if word.contains(j) {
                    points += key
                }

You would need to add in the points 3 times if the word contained j 3 times, for example.

What words are you testing? It seems like the issue is that you're likely comparing lowercased characters to your uppercased Scrabble characters.

With that said, I think your solution is a little over-complicated. For instance, why are values in scrabblePoints an array? If the values were just Strings, you could eliminate one of your for loops in generatePoints.

More importantly, let's rethink the way you've setup scrabblePoints. The point of a Dictionary is to provide you with direct lookup. By iterating over the characters, you're removing the benefit of a Dictionary. What if, instead, you structured your Dictionary like so:

static let scrabblePoints = ['A'  : 1,
                       'B'  : 3,
                       'C'  : 3,
                       // and so on for all 26 letters
                       ]

This way, you directly lookup the point value for any given character. So your generatePoints becomes:

func pointGenerator(word: String) -> Int {
    for char in word {
        points += Scrabble.scrabblePoints[char.uppercased()]
    }
    return points
}

Make a function in Scrabble that returns the point for a character by finding it in the dictionary

static func point(for character: Character) -> Int {
    Scrabble.scrabblePoints.first(where: { $0.value.contains(character.uppercased()) })?.key ?? 0
}

Then use it in your function

func pointGenerator(word:String) -> Int {
    word.reduce(into: 0, { $0 += Scrabble.point(for: $1) })
}

Note that I simplified this by changing your dictionary to be of type [Int: String],

static let scrabblePoints = [1  : "AEIOULNRST",
                             2  : "DG",
                             3  : "BCMP",
...

Try this:

       func pointGenerator(word:String) -> Int {
        for (key, letters) in Scrabble.scrabblePoints {
            for subWord in letters {
                
                     if word.contains(subWord) {
                        points += key
//                        print(points)
                    }
                }
            }
//        print(points)

        return points
    }
Related