Filter Array of Objects with Multiple Criteria and Types in Swift

Filter array of objects with multiple criteria and types in Swift

Here's how I would do this:

struct Person {
let firstName: String
let lastName: String
let age: Int
let skills: [String]

enum Filter {
enum FilterType<T: Hashable> {
case one(of: [T])
case all(of: [T])

// Match against a property that's a single value
func matches(_ value: T) -> Bool {
switch self {
case .one(let filterValues): return filterValues.contains(value)
case .all(let filterValues): return filterValues.count == 1 && filterValues[0] == value
}
}

// Match against a property that's a list of values
func matches(_ values: [T]) -> Bool {
switch self {
case .one(let filterValues): return !Set(filterValues).intersection(values).isEmpty
case .all(let filterValues): return Set(filterValues).isSuperset(of: values)
}
}
}

case age(is: FilterType<Int>)
case skills(is: FilterType<String>)

func matches(_ p: Person) -> Bool {
switch self {
case .age(let filterValues): return filterValues.matches(p.age)
case .skills(let filterValues): return filterValues.matches(p.skills)
}
}
}
}

extension Array where Element == Person.Filter {
func atLeastOneMatch(_ p: Person) -> Bool {
self.contains(where: { $0.matches(p) })
}

func matchesAll(_ p: Person) -> Bool {
self.allSatisfy { $0.matches(p) }
}
}

let people = [
Person(
firstName: "John",
lastName : "Smith",
age: 21,
skills: ["C#", "Java", "Swift"]
),
Person(
firstName: "Kim",
lastName : "Smith",
age: 28,
skills: ["Java", "Swift"]
),
Person(
firstName: "Kate",
lastName: "Bell",
age: 24,
skills: ["C#"]
),
]

let filters: [Person.Filter] = [
.age(is: .one(of: [28, 24])),
.skills(is: .one(of: ["Java", "Swift"])),
]

let peopleWhoMatchAllFilters = people.filter(filters.matchesAll)
print(peopleWhoMatchAllFilters)

let peopleWhoMatchAtLeastOneFilter = people.filter(filters.atLeastOneMatch)
print(peopleWhoMatchAtLeastOneFilter)

I've extended the filtering capability to be able to specify wether all values of a filter should be matched (e.g. a person must know Java AND Swift AND C#) or at least one (e.g. a person must know AT LEAST Java OR Swift OR C#)

Filter by multiple array conditions

Forget about the filter for a moment. Think how you would check if a car's color is a value in an array.

let colors = [ "Green", "Blue" ]
// or let colors: Set = [ "Green", "Blue" ]
if colors.contains(someCar.color) {
}

Simple enough. Now use that same simple expression in the filter.

let filterdObject = cars.filter { $0.model == currModel || colors.contains($0.color) }

Filter Array in Swift based on multiple properties

let filteredArray = users.filter({ $0.firstName.lowercased().contains("firstName") || $0.lastName.lowercased().contains("lastName") || ... })

Swift - Search large array based on multiple conditions

I found the way. I combined .filter and .sort to get the result:

func filterContentForSearchText(_ searchText: String, scope: String = "All") {

let options = NSString.CompareOptions.caseInsensitive

if scope == "All"{

print("filtering All")
self.filteredItems = allItems
.filter{$0.title.range(of: searchText, options: options) != nil && $0.allCat == scope}
.sorted{ ($0.title.hasPrefix(searchText) ? 0 : 1) < ($1.title.hasPrefix(searchText) ? 0 : 1) }
}
else{

print("filtering \(scope)")
self.filteredItems = allItems
.filter{$0.title.range(of: searchText, options: options) != nil && $0.category == scope}
.sorted{ ($0.title.hasPrefix(searchText) ? 0 : 1) < ($1.title.hasPrefix(searchText) ? 0 : 1) }

}
tableView.reloadData()
}

Hope this helps somebody

Filter two values in array of objects

You need your filter as follows (shown on multiple lines for clarity):

let outputfiler = array.filter({
$0.group.contains(where: { $0 == "groupBig" }) ||
$0.name.contains("Eis")
})

You had the filter's closing } before the ||. And assuming name is a String, contains just takes the string to search, not a closure.

Swift - Sort array of objects with multiple criteria

Think of what "sorting by multiple criteria" means. It means that two objects are first compared by one criteria. Then, if those criteria are the same, ties will be broken by the next criteria, and so on until you get the desired ordering.

let sortedContacts = contacts.sort {
if $0.lastName != $1.lastName { // first, compare by last names
return $0.lastName < $1.lastName
}
/* last names are the same, break ties by foo
else if $0.foo != $1.foo {
return $0.foo < $1.foo
}
... repeat for all other fields in the sorting
*/
else { // All other fields are tied, break ties by last name
return $0.firstName < $1.firstName
}
}

What you're seeing here is the Sequence.sorted(by:) method, which consults the provided closure to determine how elements compare.

If your sorting will be used in many places, it may be better to make your type conform to the Comparable protocol. That way, you can use Sequence.sorted() method, which consults your implementation of the Comparable.<(_:_:) operator to determine how elements compare. This way, you can sort any Sequence of Contacts without ever having to duplicate the sorting code.

How to filter object array on condition in nested array (to-many relationship) in iOS Swift

So once I edited my question in response to the suggestions here, and talked it out more, I realized the issue was simply a matter of being more specific with the type in the NSSet. So I changed the code to the following and that now works. I wanted to post that solution here in case others run into the same challenge:

    let personsReceivingGrants = (persons.schoolsApplied as Set<School>).filter() {

$0.schoolType?.awardType?.name == "Grant Award"
}

How to create a Swift filter query based on multiple conditions?

I am having a hard time understanding precisely what you want to achieve in particular. However, here is how the filter function plays out:

Given that users is an array of elements of type:

struct User {
let age: Int
let isMale: Bool
// ...
}

You can filter like so:

filteredUsers = users.filter { user in
return (user.age <= maxAge && user.age >= minAge) && /* include any other condition you need */
}

Check Swift array elements for multiple conditions

(["foo", "bar"] as Set).isSubset( of: myArr.map(\.property) )
(["foo", "bar"] as Set).isSubset( of: arr.map(\.foo) )


Related Topics



Leave a reply



Submit