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) }
Answer from rmaddy on Stack OverflowForget 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) }
Treat the filter closures like a value type and store them in an array. Use the inner reduce call to create a single boolean value that is true is all of the conditions are met by the current car. If you need a compound test like color == "blue" or "green" then simply add that to your filter closure conditions array.
struct Car {
let model: String
let color: String
}
let conditions: [(Car) -> Bool] = [
{$0.model == "Opel"},
{$0.color == "Red"},
]
let carLot = [
Car(model: "Opel", color: "Green"),
Car(model: "Mustang", color: "Gold"),
Car(model: "Opel", color: "Red"),
]
let allRedOpels = carLot.filter {
car in
conditions.reduce(true) { $0 && $1(car) }
}
Videos
Can you filter an array in Swift?
How do you apply a filter on an array of objects in Swift?
How does the filter() function work in Swift?
You would have an easier time by simplifying and breaking up your code into smaller pieces. There's no reason why a function to filter an array by some conditions, also has to be responsible for figuring out if an element meets those conditions. You've mentally trapped yourself thinking that the filter predicate has be one one long chain of && conditions in a closure.
struct CarOffer {
let brand: String
let price: Int
let color: String
let consumption: Int
}
struct CarFilter {
let brand: String?
let price: Int?
let consumption: Int?
func matches(car: CarOffer) -> Bool {
if let brand = self.brand, brand != car.brand { return false }
if let price = self.price, price != car.price { return false }
if let consumption = self.consumption, consumption != car.consumption { return false }
return true
}
}
extension Sequence where Element == CarOffer {
func filter(carFilter: CarFilter) -> [CarOffer] {
return self.filter(carFilter.matches)
}
}
let filter = CarFilter(brand: nil, price: nil, consumption: nil)
let offers: [CarOffer] = [] //...
let filteredOffers = offers.filter(carFilter: filter)
You can simply use a default value instead of filters Optional values. If you use the default value of the offer, filter will simply return true in case the optional properties were nil.
func applyFilter(filter: Filter) -> [CarOffer] {
let filteredOffers = offers.filter { $0.brand == filter.brand && $0.price <= (filter.price ?? $0.price) && $0.consumption <= (filter.consumption ?? $0.consumption) }
return filteredOffers
}
First of all I made a little bit changes to your model:
struct Student {
var fName: String?
var lname: String?
var `class`: String?
var pincode: Int?
var active: Bool
var location: Address
}
struct Address {
var street1: String?
var street2: String?
var country: String?
}
Building dummy objects:
let address1 = Address(street1: "Kiwi str", street2: nil, country: "US")
let address2 = Address(street1: "Cherry str", street2: nil, country: "GB")
let student1 = Student(fName: "Mark", lname: nil, active: false, location: address1)
let student2 = Student(fName: "Diana", lname: "Luck", active: true, location: address2)
let student3 = Student(fName: "Gaga", lname: "Merry", active: true, location: address2)
let student4 = Student(fName: "Mark", lname: "Luck", active: true, location: address1)
let students = [student1, student2, student3, student4]
Filtering extension on Array with students elements:
extension Array where Element == Student {
func filtered(fName: String? = nil, lName: String? = nil, active: Bool? = nil, street1: String? = nil) -> [Student] {
students.filter { student in
var fNameOk = false
if let fName = fName, student.fName == fName {
fNameOk = true
}
var lNameOk = false
if let lName = lName, student.lname == lName {
lNameOk = true
}
var activeOk = false
if let active = active, student.active == active {
activeOk = true
}
var street1Ok = false
if let street1 = street1, student.location.street1 == street1 {
street1Ok = true
}
return (
(fName == nil ? true : fNameOk) &&
(lName == nil ? true : lNameOk) &&
(active == nil ? true : activeOk) &&
(street1 == nil ? true : street1Ok)
)
}
}
}
Testing filtering extension:
let r1 = students.filtered(fName: "Diana", lName: "Luck", active: true)
let r2 = students.filtered(fName: "Mark", active: true, street1: "Kiwi str")
Test output:
// r1
{fName "Diana", lname "Luck", nil, nil, active true, {street1 "Cherry str", nil, country "GB"}}
// r2
{fName "Mark", lname "Luck", nil, nil, active true, {street1 "Kiwi str", nil, country "US"}}
Fixing code by Ramis so it would compile and would be more straightforward without unnecessary checking of nil values. Also, if one condition is false, it returns false immediately.
extension Array where Element == Student {
func filter(fName: String? = nil, active: Bool? = nil, street1: String? = nil) -> [Student] {
filter { student in
if let fName = fName, student.fName != fName {
return false
}
if let active = active, student.active != active {
return false
}
if let street1 = street1, student.location.street1 != street1 {
return false
}
return true
}
}
}
Usage would be i.e.:
let address1 = Address(street1: "Kiwi str", street2: nil, country: "US")
let address2 = Address(street1: "Cherry str", street2: nil, country: "GB")
let student1 = Student(fName: "Mark", lname: nil, active: false, location: address1)
let student2 = Student(fName: "Diana", lname: "Luck", active: true, location: address2)
let student3 = Student(fName: "Gaga", lname: "Merry", active: true, location: address2)
let student4 = Student(fName: "Mark", lname: "Luck", active: true, location: address1)
let students = [student1, student2, student3, student4]
let filtered1 = students.filter(fName: "Diana", active: true)
let filtered2 = students.filter(fName: "Mark")
let filteredArray = users.filter({ $0.firstName.lowercased().contains("firstName") || $0.lastName.lowercased().contains("lastName") || ... })
You can set multiple conditions and combine them together with OR (||) or AND (&&)- its a simple boolean, you can think of it as it was in an if statement-
if user.firstName.lowercased().contains("john") || user.lastName.lowerCased().contains("lastname") { return true }
else { return false }
so in your code it will be like
let filteredArray = users.filter { (user) -> Bool in
return user.firstName.lowercased().contains("john") || user.lastName.lowercased().contains("lastname") }
One approach is to update your filter to see if any value in pets is in the petArr array:
users = users.filter { $0.pets.contains(where: { petArr.contains($0) }) }
The first $0 is from the filter and it represents each User.
The second $0 is from the first contains and it represents each pet within the pets array of the current User.
If elements inside the internal array are Equatable you can just write:
array1 = array1.filter{ $0.arrayInsideOfArray1 == array2 }
If they are not, you can make them, by adopting Equatable protocol and implementing:
func ==(lhs: YourType, rhs: YourType) -> Bool
Use contains instead:
let arr = ["Hello","Bye","Halo"]
let filtered = arr.filter { $0.contains("lo") }
print(filtered)
Output
["Hello", "Halo"]
Thanks to @user3441734 for pointing out that functionality is of course only available when you import Foundation
In Swift 3.0
let terms = ["Hello","Bye","Halo"]
var filterdTerms = [String]()
func filterContentForSearchText(searchText: String) {
filterdTerms = terms.filter { term in
return term.lowercased().contains(searchText.lowercased())
}
}
filterContentForSearchText(searchText: "Lo")
print(filterdTerms)
Output
["Hello", "Halo"]
struct Address {
var name: String
var imageURL: String
var address: String
}
let VIPArray = [["name": "John B"], ["name": "Sara K"]]
let AddressArray = [Address(name: "John B", imageURL: "johnb", address: "178 Main St."),
Address(name: "Dave H", imageURL: "daveh", address: "1011 Victoria St.."),
Address(name: "Sara K", imageURL: "sarak", address: "279 Maple Av."),
Address(name: "Niles K", imageURL: "nilesk", address: "45 King St."),
Address(name: "Ingrid G", imageURL: "ingridg", address: "33 Union St.")]
var filtered = [Address]()
for element in VIPArray {
for address in AddressArray {
if element["name"] == address.name {
filtered.append(address)
}
}
}
for record in filtered {
print(record)
}
OUTPUT:
Address(name: "John B", imageURL: "johnb", address: "178 Main St.")
Address(name: "Sara K", imageURL: "sarak", address: "279 Maple Av.")
Or:
let filtered: [Address] = AddressArray.filter { (address) -> Bool in
for vip in VIPArray {
if vip["name"] == address.name {
return true
}
}
return false
}
let VIPArray = [["name": "John B"], ["name": "Sara K"]]
struct Address {
let name: String
let imageURL: String
let address: String
}
let addressArray = [Address(name: "John B", imageURL: "johnb", address: "178 Main St."),
Address(name: "Dave H", imageURL: "daveh", address: "1011 Victoria St.."),
Address(name: "Sara K", imageURL: "sarak", address: "279 Maple Av."),
Address(name: "Niles K", imageURL: "nilesk", address: "45 King St."),
Address(name: "Ingrid G", imageURL: "ingridg", address: "33 Union St.")]
let myVips = addressArray.filter() {VIPArray.contains(["name":$0.name])}