Home > Software design >  Swift filter an array inside a struct by another array
Swift filter an array inside a struct by another array

Time:05-20

I've got an array of Profile's (defined from a custom struct), and inside this, I have a [String] of interests.

I want to filter out any profiles which don't match an array of preferences, but can't seem to get my filtering to work.

The error I get is

"Cannot convert value of type '[String]' to expected argument type 'String'.

What am I missing?

Once complete, I'd expect userProfiles to contain only John and Harry.

struct Profile {
    var name: String
    var interests: [String]
}


var userProfiles: [Profile] = [
    Profile(name: "John", interests: ["Basketball", "Football", "Rowing"]),
    Profile(name: "Andrew", interests: ["Cycling", "Swimming", "Boxing"]),
    Profile(name: "Harry", interests: ["Hockey", "Baseball"])
]


let preferenceInterests = ["Basketball", "Hockey"]


userProfiles = userProfiles.filter({ (userProfile) -> Bool in
    print("\(userProfile.name): \(userProfile.interests)")
    return preferenceInterests.contains(userProfile.interests) //error here
})

CodePudding user response:

Your problem is that userProfile.interests is an array, so it wouldn't be the element of preferencesInterests which contains String.

If you think about it, you are trying to see if two sets of interests have anything in common. This is Set intersection. Sets are more efficient for this purpose than arrays because checking inclusion is O(1) instead of O(n).

By making preferenceIntests into a Set<String> you can use .isDisjoint(with:) to see if there are any interests in common. As soon as Swift finds a single interest in common, it will return false because the sets contain a common element and are not disjoint. You'll want to invert that since you want a common interest to return true:

let preferenceInterests: Set = ["Basketball", "Hockey"]

userProfiles = userProfiles.filter({ (userProfile) -> Bool in
    print("\(userProfile.name): \(userProfile.interests)")
    return !preferenceInterests.isDisjoint(with: userProfile.interests)
})

CodePudding user response:

The problem is that userProfile.interests is an array itself. Based on your example and expected output of John and Harry, it seems what you are actually trying to achieve is to keep the users who have at least 1 matching interest compared to your preferenceInterests input.

To achieve that, you need to change the body of your filter and use contains(where:) instead of contains to check if there is a match in interests.

let filteredUsers = userProfiles.filter { userProfile in
    preferenceInterests.contains(where: { interest in userProfile.interests.contains(interest) })
}
  • Related