# Registering Pokémon weaknesses

I recently answered a question that had me wondering if I could simplify this below code without creating an extra function:

for object in objects {
if let type = object["type"] where !type.isEmpty, let name = object["name"] {

print(pokemonTypeDefenseChart[type])
for weakness in pokemonTypeDefenseChart[type]! {
if pokemonWeaknessChart[weakness] == nil {
pokemonWeaknessChart[weakness] = []
}
pokemonWeaknessChart[weakness]?.append(name)
}
}

if let typeTwo = object["typeTwo"] where !typeTwo.isEmpty, let name = object["name"] {
for weakness in pokemonTypeDefenseChart[typeTwo]! {
if pokemonWeaknessChart[weakness] == nil {
pokemonWeaknessChart[weakness] = []
}
pokemonWeaknessChart[weakness]?.append(name)
}
}
}


Essentially the code loops through objects (list of pokemon) and is designed to add all of the pokemon weak to type (and optionally typeTwo).

I ended up just refactoring the for statements inside the if let statements into a function, but I was wondering if it was possible to compress these without an extra function. The only difference is the type.

I know I could simplify it a bit by using a map instead of for loops but I wanted to keep it more understandable for the question.

Any ideas?

You could do something like:

for x in ["type", "typeTwo"] {
if let type = object[x] where !type.isEmpty,
let name = object["name"],
defenseChart = pokemonTypeDefenseChart[type] { /* ... */ }
}


Also, to simplify the rest of your code, you could extend Dictionary like this:

extension Dictionary {
subscript(key: Key, fallback fallback: Value) -> Value {
get { return self[key] ?? fallback }
set { self[key] = newValue }
}
}


This allows you to replace this:

for weakness in defenseChart {
if pokemonWeaknessChart[weakness] == nil {
pokemonWeaknessChart[weakness] = []
}
pokemonWeaknessChart[weakness]?.append(name)
}


by this:

for weakness in defenseChart {
pokemonWeaknessChart[weakness, fallback: []].append(name)
}

• Is there a reason that you provide a set for the Dictionary.subscript(_:fallback:)? It seems like that that interface is not contributing anything. Aug 12, 2016 at 19:35
• @CAD97 By providing both a getter and a setter, we can apply mutating functions to that subscript (in this case append). Aug 12, 2016 at 19:47
• @TimVermeulen ah, that makes sense. Aug 12, 2016 at 19:48

I can't test this right now, but something like this might work, I am assuming each type in pokemonTypeDefenseChart is an array, so I combine the arrays , then loop through it once. Normally I would filter out dups, but you can't add functions, so I check if the name does not exist, and append if it doesn't

for object in objects {
if let name = object["name"] {
let defense = (
(object["type"].flatMap({pokemonTypeDefenseChart[$0]}) ?? []) + (object["typeTwo"].flatMap {pokemonTypeDefenseChart[$0]} ?? []))

for weakness in defense {
if pokemonWeaknessChart[weakness] == nil {
pokemonWeaknessChart[weakness] = []
}
if (pokemonWeaknessChart[weakness]?.contains(name)) == nil {
pokemonWeaknessChart[weakness]?.append(name)
}
print(pokemonWeaknessChart[weakness] )
}
}

}


Thanks to @TimVermeulen for cleaning up some parts of the answer

• I am assuming that if I call defense chart on isEmpty, that I am getting a nil result Aug 12, 2016 at 19:29
• type ?? "" doesn't make sense, because type is non-optional at that point. Same applies to typeTwo. Aug 12, 2016 at 19:49
• @TimVermeulen why wouldnt it be optional, we do not know if object["type"] exists Aug 12, 2016 at 19:52
• @TimVermeulen I added a space to help distinguish the brace (This is why I like the brace on the next line lol) Aug 12, 2016 at 19:54
• Oh! The double space between if and let might have added to the confusion, because it aligns the let statements. Either way, why not add type and typeTwo to the if let? Aug 12, 2016 at 19:57