Reputation: 6288
I'm trying to learn some ideas of functional programming, as they exist in Swift.
In a recent question, it was shown how much better this can be, by Rickster (the guru).
from this:
var voiceToUse: AVSpeechSynthesisVoice?
let voices = AVSpeechSynthesisVoice.speechVoices()
for voice in voices {
if voice.name == "Arthur"
{
voiceToUse = voice
}
}
to this:
let voiceToUse = AVSpeechSynthesisVoice.speechVoices().filter({ $0.name == "Arthur" }).first
Now I'm wondering how this technique can be applied to a problem where there's multiple criteria. Can this functional style be done to this:
var voiceToUse: AVSpeechSynthesisVoice?
let voices = AVSpeechSynthesisVoice.speechVoices()
for voice in voices {
if voice.name == "Samantha (Enhanced)" && voice.quality == .enhanced
{
voiceToUse = voice
}
}
Upvotes: 2
Views: 602
Reputation: 874
If you want the first element of the array that satisfies the criteria use this.
if let voiceToUse = AVSpeechSynthesisVoice.speechVoices().first(where: {
$0.name == "Samantha (Enhanced)" && $0.quality == .enhanced
}){
//use the voiceTouse Variable
}
If you want the last of the element of the array that satisfies the criteria use this.
if let voiceToUse = AVSpeechSynthesisVoice.speechVoices().reversed().first(where: {
$0.name == "Samantha (Enhanced)" && $0.quality == .enhanced
}){
//use the voiceTouse Variable
}
yes sure, we can use guard let ...
A guard statement is used to transfer program control out of a scope if one or more conditions aren’t met. in this example, if the condition is not met, ie there is no element in AVSpeechSynthesisVoice.speechVoices() that meet the criteria, guard let will transfer the program control out of the check function ,else if there is the some element in AVSpeechSynthesisVoice.speechVoices() that meet the criteria, program control goes to the next line after the guard let statement
func check(){
guard let voiceToUse = AVSpeechSynthesisVoice.speechVoices().first(where: {
$0.name == "Samantha (Enhanced)" && $0.quality == .enhanced
})else{
return
}
//use the voiceToUseVariable
}
check()
Upvotes: 1
Reputation: 3172
Your single condition expression uses two shortcuts in the closure syntax:
1 - If there is a single expression, it's value is returned implicitly
2 - $0
refers to the first argument - the compiler infers it's type
So, you can expand the expression to multiple clauses as long as it's still a single expression:
let voiceToUse = AVSpeechSynthesisVoice.speechVoices().filter({ $0.name == "Samantha (Enhanced)" && $0.quality == .enhanced }).first
The equivalent without the two shortcuts is this:
let voiceToUseB = AVSpeechSynthesisVoice.speechVoices().filter(
{ voice in
let found:Bool = voice.name == "Samantha (Enhanced)" && voice.quality == .enhanced
return found
}).first
Upvotes: 4
Reputation: 1042
Note that this returns a value of AVSpeechSynthesisVoice?
, so make sure you account for that.
let voiceToUse = AVSpeechSynthesisVoice.speechVoices().first(where: { $0.name == "Samantha (Enhanced)" && $0.quality == .enhanced })
Upvotes: 2