CristianMoisei
CristianMoisei

Reputation: 2279

Swift How could I safely unwrap an optional property when filtering an array?

I am trying to filter an array of objects with an optional property of the objects' class, so I was wondering what's the best way to unwrap this property safely without providing a default value. The property is of type Date so providing an alternative value feels like a hack, but I'm not sure how to do it better. I know how to safely unwrap a regular optional with guard but I'm not sure how to use this when filtering an array. The code I have is this:

let completedGoalsThisWeek = goals.filter { $0.returnWeek(date: $0.dateAchieved) == deviceWeek }.count

The property in question is dateAchieved and it will be nil in a lot of circumstances.

Thank you.

Upvotes: 3

Views: 3835

Answers (2)

Shehata Gamal
Shehata Gamal

Reputation: 100533

Error here

$0.returnWeek(date: $0.dateAchieved) == deviceWeek 

can ve solved by making paramter date an optional which will make the return optional too but it will pass as you can compare an optional value with non-one


returnWeek Would be like

func returnWeek(date:Date?) -> TypeOFWeek? {
  guard let res = date else { return nil }
  // here return valid result 
}

OR

let completedGoalsThisWeek = goals.filter { $0.dateAchieved != nil ?  ( $0.returnWeek(date: $0.dateAchieved!) == deviceWeek ) : false }.count

Or better the model

class Model { 
  var toWeek: TypeOfWeek? {
    // do where what in function which uses dateAchieved
  }
}

let completedGoalsThisWeek = goals.filter { $0.toWeek  == deviceWeek }.count

Upvotes: 2

Alexander
Alexander

Reputation: 63321

There's nothing special to it. Just unwrap the optional like you would in any other case:

let completedGoalsThisWeek = goals
    .lazy
    .filter { goal -> Bool in
        guard let dateAchieved = goal.dateAchieved else { return false }
        let isCurrentWeek = goal.returnWeek(date: dateAchieved) == deviceWeek
        return isCurrentWeek
    }.count

You could use Optional.map to shorten this, but I would advise against it, it's too cryptic:

let completedGoalsThisWeek = goals
    .lazy
    .filter { goal
        goal.dateAchieved.map { goal.returnWeek(date: $0) == deviceWeek } ?? false
    }.count

In either case, I suggest you use .lazy.filter, to prevent the intermediate allocation to hold an array of elements, if you're ultimately going to only count them and immediately discard them.

Upvotes: 4

Related Questions