This is what I have working in a playground, any reason why this is no good?
class Book {
var author = String()
init(author:String){
self.author = author
}
}
var allBooks: [Book] = []
allBooks.append(Book(author: "John Smith"))
allBooks.append(Book(author: "Arthur Price"))
allBooks.append(Book(author: "David Jones"))
allBooks.append(Book(author: "Somebody Else"))
let authors = ["Arthur Price", "David Jones"]
let filteredBooks = allBooks.filter({authors.contains($0.author)})
filteredBooks // [{author "Arthur Price"}, {author "David Jones"}]
Answer from Jon-Paul on Stack OverflowThis is what I have working in a playground, any reason why this is no good?
class Book {
var author = String()
init(author:String){
self.author = author
}
}
var allBooks: [Book] = []
allBooks.append(Book(author: "John Smith"))
allBooks.append(Book(author: "Arthur Price"))
allBooks.append(Book(author: "David Jones"))
allBooks.append(Book(author: "Somebody Else"))
let authors = ["Arthur Price", "David Jones"]
let filteredBooks = allBooks.filter({authors.contains($0.author)})
filteredBooks // [{author "Arthur Price"}, {author "David Jones"}]
You could also use something like
let authorsAndBooks = authors.map {
(authorName) -> (String, [Book])
in (authorName,
allBooks.filter({ $0.author == authorName })
)
}
This will array of tuples with the first element being the author name and the second element an array of his books, in case an author wrote more than one book.
I have an array of Match objects [Match] with a string property of matchID. I also have an array of strings.
I would like to remove the Match object where matchID equals any string in the array.
I currently have:
var matches = [Match]
var matchIDsToDelete = [String]
for match in matches {
for id in matchIDsToDelete {
if match.matchID == id {
// remove that item from array
}
}
}My concern is removing the object from the array as I'm enumerating. I believe there will be consequences. Any suggestions? I would also love a functional solution as I'm currently reading more about functional programming.
Thanks!
contains() checks if a sequence contains a given element, e.g.
if a String contains a given Character.
If your intention is to find all books where the name contains the substring "rt", then you can use rangeOfString():
var arr = englishBooks.filter {
$0.nameOfBook.rangeOfString("rt") != nil
}
or for case-insensitive comparison:
var arr = englishBooks.filter {
$0.nameOfBook.rangeOfString("rt", options: .CaseInsensitiveSearch) != nil
}
As of Swift 2, you can use
nameOfBook.containsString("rt") // or
nameOfBook.localizedCaseInsensitiveContainsString("rt")
and in Swift 3 this is
nameOfBook.contains("rt") // or
nameOfBook.localizedStandardContains("rt") // or
nameOfBook.range(of: "rt", options: .caseInsensitive) != nil
Sorry this is an old thread. Change you code slightly to properly init your variable 'nameOfBook'.
class book{
var nameOfBook: String!
init(name: String) {
nameOfBook = name
}
}
Then we can create an array of books.
var englishBooks = [book(name: "Big Nose"), book(name: "English Future
Prime Minister"), book(name: "Phenomenon")]
The array's 'filter' function takes one argument and some logics, 'contains' function can take a simplest form of a string you are searching for.
let list1 = englishBooks.filter { (name) -> Bool in
name.contains("English")
}
You can then print out list1 like so:
let list2 = arr1.map({ (book) -> String in
return book.nameOfBook
})
print(list2)
// print ["English Future Prime Minister"]
Above two snippets can be written short hand like so:
let list3 = englishBooks.filter{ ($0.nameOfBook.contains("English")) }
print(list3.map({"\($0.nameOfBook!)"}))
try this
let filteredArray = self.originalArray.filter({($0.itemCategory.localizedCaseInsensitiveContains(searchText))!})
You can try something like:
itemArray.filter({$0.itemCategory == "Test"})
$0 will present the object in the array and you can use it for every property in your object.
This is unnecessarily complex. There's no need for NSPredicate here.
var studios = [Studio]()
var filteredStudios = [Studio]()
var studiosToDisplay = [Studio]()
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchText = searchController.searchBar.text
print("SEARCH TEXT: \(searchText)")
if let searchText = searchText, !searchText.isEmpty {
studiosToDisplay = studios.filter{ $0.studioName.contains(searchText) }
}
else {
studiosToDisplay = studios
NSNotificationCenter.defaultCenter().postNotificationName("showResultsBeforeSearchingNotification", object: nil) //Calls SearchVC
}
self.resultTableView.reloadData()
}
Thanks to @Alexander .. Here's the code I ended up using:
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchText = searchController.searchBar.text
if let searchText = searchText {
if !searchText.isEmpty {
self.studiosToDisplay = self.studios.filter { $0.studioName!.containsString(searchText) }
for studio in self.studiosToDisplay {
print("\(studio.studioName!)")
}
}
else {
self.studiosToDisplay = self.studios
NSNotificationCenter.defaultCenter().postNotificationName("showResultsBeforeSearchingNotification", object: nil) // Calls SearchVC
}
}
self.resultTableView.reloadData()
}
I figured out, that my former code, with the NSPredicate actually was functional - I was just presenting the wrong array in my tableview.. Oops.. But now it works, and I stick to the more simple code. Don't know why I didn't think of using the $0-function :) .. Anyway, thanks!
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
You have no way to prove at compile-time that there is only one possible result on an array. What you're actually asking for is the first matching result. The easiest (though not the fastest) is to just take the first element of the result of filter:
let imageObject = questionImageObjects.filter{ $0.imageUUID == imageUUID }.first
imageObject will now be an optional of course, since it's possible that nothing matches.
If searching the whole array is time consuming, of course you can easily create a firstMatching function that will return the (optional) first element matching the closure, but for short arrays this is fine and simple.
As charles notes, in Swift 3 this is built in:
questionImageObjects.first(where: { $0.imageUUID == imageUUID })
Edit 2016-05-05: Swift 3 will include first(where:).
In Swift 2, you can use indexOf to find the index of the first array element that matches a predicate.
let index = questionImageObjects.indexOf({$0.imageUUID == imageUUID})
This is bit faster compared to filter since it will stop after the first match. (Alternatively, you could use a lazy sequence.)
However, it's a bit annoying that you can only get the index and not the object itself. I use the following extension for convenience:
extension CollectionType {
func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
return try indexOf(predicate).map({self[$0]})
}
}
Then the following works:
questionImageObjects.find({$0.imageUUID == imageUUID})
import UIKit
First of all you don't need to inherit from NSObject. Also, if you don't need reference semantics, use structs instead.
struct TicketsCellModel {
var title: String?
var text: String?
var price: String?
var tintColor: UIColor?
var quantity: Int?
}
It's not really necessary to use a closure to create an [TicketsCellModel]. Just assign the elements directly. Since we're using structs we don't need to create a separate init.
var ticketCellModels = [
TicketsCellModel(
title: "Standard Entry",
text: "This is aa standard entry ticket, it's not sutiable for special events please see the plus ticket for that.",
price: "£8.35",
tintColor: UIColor.white,
quantity: 0
),
TicketsCellModel(
title: "Standard with re-entry",
text: "This is a standard entry ticket but you can come and go as you please during the night.",
price: "£8.99",
tintColor: UIColor.white,
quantity: 2
),
TicketsCellModel(
title: "Plus Entry",
text: "This is the plus entry ticket for special events.",
price: "£9.99",
tintColor: UIColor.white,
quantity: 0
),
TicketsCellModel(
title: "VIP Entry",
text: "Here is some more text that is to act as a description for this thing you will purchase.",
price: "£12.99",
tintColor: UIColor.white,
quantity: 4
)
]
Now, if you need to access an optional, you will have to unwrap it first. The safest way to do this is using the if let construct or using the nil-coalescing operator.
let filteredTicketCellModels = ticketCellModels.filter { $0.quantity ?? 0 > 0 }
print(filteredTicketCellModels)
In the example above there are no unknown variables during initialization, so maybe non-optional properties might be better suited. Then you don't have to unwrap anything.
Leo Dabus adds that it is recommended that all properties be constants. The way to do this is to replace all vars with lets. If you need to change a property you can create a new object copying the properties from the old one and adding the new values to the properties that have changed.
If you want a quick and safe fix, you can use the nil coalescing operator ?? to have item.quantity = 0 when it's equal to nil.
It'd look like this:
let filteredTicketCellModels = ticketCellModels.filter( { return ($0.quantity ?? 0 > 0)! } )
for item in filteredTicketCellModels {
print("qty: \(item.quantity)")
}
The reason why your filtering by titles doesn't crash is because none of your optional variables contain nil. If item.title were == to nil however, that bang (!) at the end of it causes a crash upon variable access.
If titles (or any other variables within your class) are never nil, you should declare them as just var title: String , without the ? at the end (which would mean you have to actually have an initializer for your object too! (which is better practice anyways :) ))
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
Hi.
I want to filter by a property inside of an array but I can't find a why to make it work. I already tried different options but none of them work. Xcode throws this error:
Cannot convert value of type 'PredicateExpressions.SequenceContainsWhere<PredicateExpressions.KeyPath<PredicateExpressions.Variable<Excercise>, [Muscle]>, PredicateExpressions.Equal<PredicateExpressions.KeyPath<PredicateExpressions.Variable<Muscle>, String>, PredicateExpressions.KeyPath<PredicateExpressions.Value<Muscle>, String>>>' to closure result type 'any StandardPredicateExpression<Bool>'
This is the code:
if let muscle {
let predicate = #Predicate<Exercise> { exercise in
exercise.muscles.contains { _muscle in
_muscle.id == muscle.id
}
}
_exercises = Query(filter: predicate)
}Models:
Exercise
@Model
final class Exercise: Identifiable {
@Attribute(.unique)
let id = UUID().uuidString
var name: String
@Relationship(deleteRule: .nullify)
let muscles: [Muscle]
init(name: String, muscles: [Muscle] = []) {
self.name = name
self.muscles = muscles
}
}Muscle
@Model
final class Muscle: Identifiable, Equatable {
@Attribute(.unique)
let id = UUID().uuidString
let name: String
let icon: String
let color: String
init(name: String, icon: String, color: String) {
self.name = name
self.icon = icon
self.color = color
}
static func == (lhs: Muscle, rhs: Muscle) -> Bool {
lhs.id == rhs.id
}
}Thanks!