Here’s another great puzzle to code a solution for.
This week’s challenge is a spinoff of my on-air puzzle. Name two countries that have consonyms that are nationalities of other countries. In each case, the consonants in the name of the country are the same consonants in the same order as those in the nationality of another country. No extra consonants can appear in either name. The letter Y isn’t used.
So if you remove all the vowels, the name of a country will equal the name of a nationality from a different country. Also, for our purposes a letter Y can be considered a vowel (ignored). Sounds good!
Internet of Trivia Things
A convenient list of all countries can be found here
…and a list of nationalities here
To the Swift!
Here’s my solution:
func doPuzzle() async throws {
let countriesUrl = Bundle.main.url(forResource: "countries", withExtension: "txt")
let countryNames = countriesUrl!.lines
var countriesNoVowels: [String: String] = [:]
for try await name in countryNames {
let noVowels = name.lowercased()
.filter({ notAVowel($0) })
.map({ "\($0)" })
.reduce("", +)
countriesNoVowels[noVowels] = name
}
let nationalitiesUrl = Bundle.main.url(forResource: "nationalities", withExtension: "txt")
let nationalityNames = nationalitiesUrl!.lines
var nationalitiesNoVowels: [String: String] = [:]
for try await name in nationalityNames {
let noVowels = name.lowercased()
.filter({ notAVowel($0) })
.map({ "\($0)" })
.reduce("", +)
nationalitiesNoVowels[noVowels] = name
}
var countriesNoVowelsSet: Set<String> = []
countriesNoVowels.keys.forEach {
countriesNoVowelsSet.insert($0)
}
nationalitiesNoVowels.keys.forEach {
if countriesNoVowelsSet.contains($0) {
print("Match! \(countriesNoVowels[$0]!) \(nationalitiesNoVowels[$0]!)")
}
}
}
func notAVowel(_ character: Character) -> Bool {
character != "a" &&
character != "e" &&
character != "i" &&
character != "o" &&
character != "u" &&
character != "y"
}
The beauty of Swift’s string implementation lets us iterate over each character of the name of a country or a nationality with a function like map or filter. As we need to remove the vowels and keep only consonants, a call to filter handles that aspect of the work, leaving us with a character. Following that, a call to map* to transform the character to string, and reduce to combine all of those single-character strings together, and we have our consonants-only names. Making these names the key values of a dictionary allows us to map them back to their original country or nationality name.
The Need? for Speed
To keep things snappy, I also build a Set
One out of Two Ain’t Bad (Also Spoilers)
So unfortunately with these data sources, we have no association between country and nationality, so we can’t automatically reject matches from the same country like Germany and German. However, if we allow our eyes to fall immediately to the end of the list of matches we see:
Match! Lebanon Albanian
…which is great! This was indeed one of the accepted answers.
Now if you look ahead to next week’s puzzle for this accepted answer, you can also see that there was a second solution that we didn’t see!
Ukraine –> Korean
Which is a shame because the puzzle stated that the solver needed to find both. The reason why our code didn’t find this answer was because in our nationalities file we have entries for
North Korean
South Korean
…but none for simply “Korean”. That’s politics for you, I suppose.
It is still a fun puzzle to code up a solution for though. So, until next week, happy solving!