Confused
Confused

Reputation: 6288

Filter, Closure, Functional syntax version of for loop with multiple conditions

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

Answers (3)

Samip Shah
Samip Shah

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

DavidA
DavidA

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

Wes
Wes

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

Related Questions