Search code examples
pythonarraysany

Python filtering array using the any() function


I am trying to write a program that recommends books by taking two inputs:

  • The maximum amount of money the customer is prepared to spend.
  • A string that consists of the first letters of all desired attributes ("horror", "scifi", "romance", "fantasy").

The desired attribute input should be a string such as sh or shf (the order should not matter therefore the string "shf" is treated the same as "fhs").

I have successfully created a filter that filters out the books that exceed the customer's maximum cost. However, the function continues to output any books that are in any of the categories provided by the user, not books that meet all of the categories as required (e.g. books that are both in fantasy and romance, for example). I've tried to replace the any() function with the all() function, but this does not seem to rectify the issue. Can anyone provide suggestions as to where I am going wrong?

def recommend_books(max_price, cats):
    if not set(cats).issubset(set(book_categories)):
        raise ValueError(f'{cats} contains an invalid category code')
    cats_f = [cat for cat_i, cat in book_categories.items() if cat_i in set(cats)]
    return [book for book, price, cats in book_data
            if (price <= max_price and all(True for cat in cats if cat in cats_f))]


book_data = [["Harry Potter", 8, ["fantasy", "romance"]],
             ["IT", 11, ["horror", "fantasy"]],
             ["Star Wars", 22, ["scifi", "romance", "fantasy"]],
             ["Carrie", 13, ["horror"]],
             ["Lord of the Rings", 29, ["fantasy", "romance"]]
             ]

book = [book[0] for book in book_data]

book_categories = {}
for book, price, category in book_data:
    for cat in category:
        cat_initial = cat[0]
        if not cat_initial in book_categories:
            book_categories[cat_initial] = cat

recommend_books(25, "hf")
# Output should be an array of the Titles of the suitable book recommendations
# I.e. ["IT"]

The output for this example is: ['Harry Potter', 'IT', 'Star Wars', 'Carrie'] which is an array of all horror and fantasy books less than 25, so the filter does not appear to be working correctly.


Solution

  • Editing my answer after your comment:

    your second condition in the filtering should be :

    all(elem in cats for elem in cats_f)
    

    and the complete function:

    def recommend_books(max_price, cats):
            if not set(cats).issubset(set(book_categories)):
                raise ValueError(f'{cats} contains an invalid category code')
            cats_f = [cat for cat_i, cat in book_categories.items() if cat_i in set(cats)]
            return [book for book, price, cats in book_data 
                    if (price <= max_price and **all (True for cat in cats if cat in cats_f))**]
    

    iterating over all the categories(cat_f) you want to have, and check if all of them are in the book's categories(cats)

    this produces your desired output.