15

What is the most efficient way of filtering an array of objects based on one of their properties, using an array of values? I could iterate through the items, but I can't help thinking there's a really efficient way using Array.filter and Array.contains - I'm just not proficient enough with Swift to be able to put the pieces together.

For example, if I have an array containing Book objects, each of which has a String author property, how would I filter it to show only books by John Smith, Arthur Price or David Jones?

Something along the lines of:

Class Book {
    var author : String = String()
}

var books : Array = [Book]()
//books added elsewhere

let authors = ["John Smith", "Arthur Price", "David Jones"]

let filteredBooks = books.filter({authors.contains({($0 as Book).author})})
3
  • Do you need the easiest way or the most effective? If the most effective then for example you need first prepare the data and convert the array to the dictionary with the key - the search field and value - the Book object. After this action your search will be the most effective Commented Oct 22, 2015 at 19:47
  • @Alex, how would you apply that method to the command I just added above? Commented Oct 22, 2015 at 19:56
  • 1
    you're working with an array, in this case, the search may not be as fast as possible, to make it as fast as possible you need to work with dictionary. If you need fast search see answer below from Arsen, there is an example of what I mean Commented Oct 22, 2015 at 20:03

3 Answers 3

20

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"}]
Sign up to request clarification or add additional context in comments.

Comments

7

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.

1 Comment

u will get multiple times the same authorName with corresponding book array everytime.
3

I'd recommend you make an index of books by author:

let book = Book(author: "Arsen")
let bookIndex = [book.author: [book]]

And now you have incredible fast access to your book filtered by author:

bookIndex["Arsen"] // => [Books]

For multiple authors:

var results = [Book]()
for author in authors {
    if let books = bookIndex[author] {
        results += books
    }
}

results

4 Comments

this looks interesting, but how would you use an index to filter by multiple values?
@Jon-Paul check out the update. I think it can be written in one-line style with map and flatmap but still it very efficient way
Thanks - and this would be quicker than working with the array as I've suggested?
To be clear, I don't need it to be as fast as possible. I'm just interested in using the best design patterns as I learn Swift. :)

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.