0

I'm looking for a nice swift solution for the following problem:

Lets say we have 2 structs like so:

struct Person {
    let name: String
    let age: Int
    let skills: Skills
    init(name: String, age: Int, skills: Skills) {
        self.name = name
        self.age = age
        self.skills = skills
    }

}

struct Skills {
    let canUseBow: Bool
    let canUseSword: Bool
    let canUseShield: Bool
    init(canUseBow: Bool, canUseSword: Bool, canUseShield: Bool) {
        self.canUseBow = canUseBow
        self.canUseSword = canUseSword
        self.canUseShield = canUseShield
    }
}

Now lets say I have an array of Person where each person has their own skills obviously where the corrosponding values can be true or false.

Lets say I want another array of just people that have the skill canUseBow as true so that skill must be set to true , how would I go about filtering out the Persons that do not have canUseBow set to true?

I was thinking in a direction of:

filteredPersons = persons.filter {
  $0.skills
}

But that way it would require me to than select something after skills for example $0.skills.canUseBow

That does not seem very future proof, lets say I would want to add more skills than I would also have to change the filter method again. Are there better ways to go about this?

10
  • I’m not following... what’s wrong with $0.skills.canUseBow? Commented Jun 20, 2021 at 18:50
  • What is your concern with filteredPersons = persons.filter { $0.skills.canUseBow } or filteredPersons = persons.filter { $0.skills.canUseBow == false } ? Commented Jun 20, 2021 at 18:50
  • The reason I figure this is not great because maybe a person has over 100 skills which I might want to filter lets say 50 I would need 50 different statements? I just doesn't seem very efficient. (I might be missing something) Commented Jun 20, 2021 at 18:57
  • How about an OptionSet ? Commented Jun 20, 2021 at 19:02
  • 1
    this?? In Swift? Commented Jun 20, 2021 at 19:20

1 Answer 1

3

You can try this with an OptionSet that can hold all of these flags for you in a simple Int storage.

import Foundation

struct Person {
    let name: String
    let age: Int
    let skills: Skills
    init(name: String, age: Int, skills: Skills) {
        self.name = name
        self.age = age
        self.skills = skills
    }
}

struct Skills: OptionSet {
    let rawValue: Int
    init(rawValue: Int) {
        self.rawValue = rawValue
    }
    
    static let canUseBow = Skills(rawValue: 1 << 0)
    static let canUseSword = Skills(rawValue: 1 << 1)
    static let canUseShield = Skills(rawValue: 1 << 2)
    
    init(json: [String: Bool]) {
        var skills = Skills(rawValue: 0)
        if let canUseBow = json["can_use_bow"], canUseBow {
            skills.insert(.canUseBow)
        }
        if let canUseSword = json["can_use_sword"], canUseSword {
            skills.insert(.canUseSword)
        }
        if let canUseShield = json["can_use_shield"], canUseShield {
            skills.insert(.canUseShield)
        }
        self = skills
    }
}

How to instantiate Skills?

let skills = Skills(json: [
    "can_use_bow" : true,
    "can_use_sword" : true,
    "can_use_shield" : false,
])

How to filter based on multiple skills?

let targetSkills: Skills = [.canUseBow, .canUseSword]

let persons: [Person] = []
let filteredPersons = persons.filter { 
    targetSkills.isSubset(of: $0.skills)
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much! This works perfectly especially being able to provide it with the JSON is exactly what I'm looking for!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.