Filter for author exact match regardless of case
Question:
I have a linked list for a catalog and book. I am trying to filter by author and return with the books that are of exact match, however, it says that my book type has no such attribute whenever i run it. I also try to upper case the author names so that it is consistent and match will return even if input are of different case
class Book:
def __init__(self, title, author, year):
if not isinstance(title, str):
raise Exception("title must be a string")
if not isinstance(author, str):
raise Exception("author must be a string")
if not isinstance(year, int):
raise Exception("year must be an integer")
self.title = title
self.author = author
self.year = year
def __eq__(self, other):
if isinstance(other, Book):
return self.title == other.title and
self.author == other.author and
self.year == other.year
return NotImplemented
def __repr__(self):
return f"{repr(self.title)} by {repr(self.author)} {self.year})"
class Catalog:
def __init__(self):
self.lst = []
def filter_by_author(self, author):
xs = self.lst.copy()
xs = [author.capitalize() for author in xs]
if author.upper() in xs:
return self.lst
# driver
b1 = Book("1984", "George Orwell", 1949)
b2 = Book("Brave new world", "Aldous Huxley", 1932)
b3 = Book("El aleph", "Jorge Louis Borges", 1949)
b4 = Book("The devils of Loudun", "Aldous Huxley", 1952)
cat = Catalog()
cat.add(b1)
cat.add(b2)
cat.add(b3)
cat.add(b4)
la = cat.filter_by_author("aldous huxley")
assert la == [b2, b4]
I am trying to assert if author matches the books in the catalog, the list will return with the books
Answers:
You don’t need to make a copy of your lst, since your generator below makes a new list anyway. I also don’t understand why you are using .capitalize()
The problem is that in your list comprehension
you go through each book
, call the current Book
"author"
and then try to captialize author
. author
however is a Book
, which you cannot capitalize. In your code you’d need to call author.author.capitalize()
, or you just use the following:
def filter_by_author(self, author):
author = author.lower()
return [book for book in self.lst if book.author.lower() == author]
Edit to respond to comment
In python you can easily check whether a string contains a certain substring:
def filter_by_author(self, author):
author = author.lower()
return [book for book in self.lst if author in book.author.lower()]
I am not sure however that is what you want, because "John" in "Johnathan"
is True. So you’d probably want to check if any of the names are "John"
def filter_by_author(self, author):
author = author.lower()
return [book for book in self.lst if author in book.author.lower().split()]
This first splits the string at a certain string. Eg. "John Nathan Last-name".split(" ") == ["John", "Nathan", "Last-name"]
The arguments default value is " "
, so you don’t need to pass it in.
add() was not defined.
str.capitalize() is different from str.upper()
do not return self.lst itself
class Catalog:
def __init__(self):
self.lst = []
def add(self, b):
self.lst.append(b)
def filter_by_author(self, author):
return [b for b in self.lst if b.author.upper() == author.upper()]
I have a linked list for a catalog and book. I am trying to filter by author and return with the books that are of exact match, however, it says that my book type has no such attribute whenever i run it. I also try to upper case the author names so that it is consistent and match will return even if input are of different case
class Book:
def __init__(self, title, author, year):
if not isinstance(title, str):
raise Exception("title must be a string")
if not isinstance(author, str):
raise Exception("author must be a string")
if not isinstance(year, int):
raise Exception("year must be an integer")
self.title = title
self.author = author
self.year = year
def __eq__(self, other):
if isinstance(other, Book):
return self.title == other.title and
self.author == other.author and
self.year == other.year
return NotImplemented
def __repr__(self):
return f"{repr(self.title)} by {repr(self.author)} {self.year})"
class Catalog:
def __init__(self):
self.lst = []
def filter_by_author(self, author):
xs = self.lst.copy()
xs = [author.capitalize() for author in xs]
if author.upper() in xs:
return self.lst
# driver
b1 = Book("1984", "George Orwell", 1949)
b2 = Book("Brave new world", "Aldous Huxley", 1932)
b3 = Book("El aleph", "Jorge Louis Borges", 1949)
b4 = Book("The devils of Loudun", "Aldous Huxley", 1952)
cat = Catalog()
cat.add(b1)
cat.add(b2)
cat.add(b3)
cat.add(b4)
la = cat.filter_by_author("aldous huxley")
assert la == [b2, b4]
I am trying to assert if author matches the books in the catalog, the list will return with the books
You don’t need to make a copy of your lst, since your generator below makes a new list anyway. I also don’t understand why you are using .capitalize()
The problem is that in your list comprehension
you go through each book
, call the current Book
"author"
and then try to captialize author
. author
however is a Book
, which you cannot capitalize. In your code you’d need to call author.author.capitalize()
, or you just use the following:
def filter_by_author(self, author):
author = author.lower()
return [book for book in self.lst if book.author.lower() == author]
Edit to respond to comment
In python you can easily check whether a string contains a certain substring:
def filter_by_author(self, author):
author = author.lower()
return [book for book in self.lst if author in book.author.lower()]
I am not sure however that is what you want, because "John" in "Johnathan"
is True. So you’d probably want to check if any of the names are "John"
def filter_by_author(self, author):
author = author.lower()
return [book for book in self.lst if author in book.author.lower().split()]
This first splits the string at a certain string. Eg. "John Nathan Last-name".split(" ") == ["John", "Nathan", "Last-name"]
The arguments default value is " "
, so you don’t need to pass it in.
add() was not defined.
str.capitalize() is different from str.upper()
do not return self.lst itself
class Catalog:
def __init__(self):
self.lst = []
def add(self, b):
self.lst.append(b)
def filter_by_author(self, author):
return [b for b in self.lst if b.author.upper() == author.upper()]