Filter an array of objects by property, using an array of values in Swift

17,573

Solution 1

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

Solution 2

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"}]

Solution 3

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.

Share:
17,573
Jon-Paul
Author by

Jon-Paul

Updated on July 28, 2022

Comments

  • Jon-Paul
    Jon-Paul over 1 year

    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})})
    
    • Alexey Pichukov
      Alexey Pichukov over 8 years
      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
    • Jon-Paul
      Jon-Paul over 8 years
      @Alex, how would you apply that method to the command I just added above?
    • Alexey Pichukov
      Alexey Pichukov over 8 years
      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
  • Jon-Paul
    Jon-Paul over 8 years
    this looks interesting, but how would you use an index to filter by multiple values?
  • Arsen
    Arsen over 8 years
    @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
  • Jon-Paul
    Jon-Paul over 8 years
    Thanks - and this would be quicker than working with the array as I've suggested?
  • Jon-Paul
    Jon-Paul over 8 years
    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. :)
  • rd_
    rd_ over 7 years
    u will get multiple times the same authorName with corresponding book array everytime.