0

I'm trying to filter a set of objects based on values in one of their elements based on another array. I've got it working with filter just fine if the search is "OR" - it returns give me all objects for which at least one of the strings in the search array is found.

But I can't figure out how to make it work as an AND search - returning only the objects that match ALL of the strings in the search array.

Example:

struct Schedule {
    let title: String
    let classTypes: [String]
}

let schedule1 = Schedule(title: "One", classTypes: ["math","english","chemistry","drama"])
let schedule2 = Schedule(title: "Two", classTypes: ["pe","math","biology"])
let schedule3 = Schedule(title: "Three", classTypes: ["english","history","math","art"])

let schedules = [schedule1, schedule2, schedule3]

let searchArray = ["math", "english"]

//works for OR - "math" or "english"
var filteredSchedules = schedules.filter { $0.classTypes.contains(where: { searchArray.contains($0) }) }

I'd like to find a way for it to use the same search array

let searchArray = ["math", "english"]

But only return items 1 & 3 - as they both have BOTH math and english in the list.

There are good examples of AND conditions when the AND is across different search criteria: car type and colour - but I've been unable to find an example where the criteria are dynamically based on items in an array. For context, I could have dozens of schedules with 20+ class types.

3
  • So you are trying to find only an exact math. It should return "One" and "Three" but not "Two" because it has only one item? Commented Jul 13, 2020 at 19:53
  • That's correct - it works fine as it is for partial match - but what I can't get is exact. Commented Jul 13, 2020 at 20:06
  • Does the order of the elements in your collection matter? Commented Jul 13, 2020 at 20:06

1 Answer 1

1

You can work with a Set, isSubset will return true if the schedules element contains all elements of the searchSet

let searchSet = Set(searchArray)
var filteredSchedules = schedules.filter { searchSet.isSubset(of: $0.classTypes) }

As suggested by @LeoDabus it might be worth changing the type of classTypes to Set instead of arrays (if order doesn't matter) since they seems to be unique and then the filtering can be done in the opposite way without the need to convert searchArray each time

var filteredSchedules = schedules.filter { $0.classTypes.isSuperset(of: searchArray) }
Sign up to request clarification or add additional context in comments.

7 Comments

I was about to suggest that. But I would post $0.classTypes.isSuperset(of: searchArray)
@LeoDabus But them classTypes needs to be changed to be a Set first, right?
Sure. The use of an array there doesn't make any sense considering there will never be duplicate items. struct Schedule { let title: String let classTypes: Set<String> }
@LeoDabus That's true and I assume order doesn't matter.
Thanks both - I'll try it out. Order does not matter - I'm just trying to find the set of schedules that have ALL types of classes that are in the filter array.
|

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.