在Swift中过滤具有多个条件和类型的对象数组 [英] Filter array of objects with multiple criteria and types in Swift
问题描述
我正在尝试在我的应用程序中进行一些复杂的过滤,但到现在我不知道下一步该怎么做.我的数据由一个字典数组组成,其中每个字典中的值可以是String
,Int
或[String]
.
I am trying to do some complex filtering in my app and I am to a point where I don't know what to do next. My data consistes of an array of dictionaries where the values in each of the dictionaries can be String
, Int
or [String]
.
let person1: [String : Any] = ["first_name" : "John",
"last_name" : "Smith",
"age" : 21,
"skills" : ["C#", "Java", "Swift"]]
let person2: [String : Any] = ["first_name" : "Kim",
"last_name" : "Smith",
"age" : 28,
"skills" : ["Java", "Swift"]]
let person3: [String : Any] = ["first_name" : "Kate",
"last_name" : "Bell",
"age" : 24,
"skills" : ["C#"]]
var people = [person1, person2, person3]
我让用户选择如何过滤此数据并创建过滤条件字典.该词典可以具有任意数量的键和值.
I let the user choose how to filter this data and create a dictionary of filter criteria. This dictionary can have any number of keys and values.
let filters: [String : [Any]] = ["age" : [28, 24],
"skills" : ["Java", "Swift"]]
在此示例中,我想显示age
28或24并具有Java或Swift的skills
的人,即person2
In this example I want to show persons who are age
28 or 24 and have a skills
of Java or Swift, which would be person2
这是我到目前为止的内容,但仅适用于Int
值:
Here is what I have so far but it only works with Int
values:
for (key, values) in filters {
var filteredItems = people.filter {
var match = false
for filterValue in values {
if $0[key] as! Int == filterValue as! Int {
match = true
break
}
else {
match = false
}
}
return match
}
people = filteredItems
}
推荐答案
这是我的处理方式:
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)
我已经扩展了过滤功能,以便能够指定过滤器的所有值是否都应匹配(例如,一个人必须知道Java AND Swift AND C#)或至少一个(例如,一个人必须知道至少Java或Swift OR C#)
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#)
这篇关于在Swift中过滤具有多个条件和类型的对象数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!