Reputation: 9347
I'm aware that there are 1001 ways of solving this solution, I'm asking the community to get an understanding of what the most Pythonic approach seems to be.
Lets say I have a list of dictionaries taking the format:
colours = [{"color": "green", "owner": "Mark"},
{"color": "blue", "owner": "Luke"},
{"color": "red", "owner": "John"}]
Overlooking the obvious fact that the list should be a dictionary of dictionaries, I'm wanting to retrieve a single dictionary from the list given a user input for the color
key in the dictionary, but using a default value if the color is not matched (lets say "green" in this example).
As such I'm seeking a function:
def get_with_default(colour, colours, default):
That given the colours list would return:
>>> get_with_default("blue", colours, "green") # Valid dictionary
{"color": "blue", "owner": "Luke"}
>>> get_with_default("black", colours, "green") # Colour doesn't exist
{"color": "green", "owner": "Mark"}
Update (thanks Martijn), the default value would be hard-coded and known to be in the list, however the other key/value pairs in that dictionary are unknown/dynamic (so I know that 'green' is a key in a dictionary, but I don't know who 'owns' green in this simplified case
Upvotes: 1
Views: 857
Reputation: 1057
You can also use list comprehension to achieve this.
def get_with_defaults(color,colours,default='green'):
res = [col for col in colours if col['color']==color]
if not res:
return [col for col in colours if col['color']==default]
return res
get_with_defaults('blue',colours)
[{'color': 'blue', 'owner': 'Luke'}]
>>> get_with_defaults('black',colours)
[{'color': 'green', 'owner': 'Mark'}]
Output in dictionary.
def get_with_defaults(color,colours,default='green'):
res = [col for col in colours if col['color']==color]
if not res:
return [col for col in colours if col['color']==default][0]
return res[0]
get_with_defaults('blue',colours)
{'color': 'blue', 'owner': 'Luke'}
>>> get_with_defaults('black',colours)
{'color': 'green', 'owner': 'Mark'}
Upvotes: 0
Reputation: 1122352
next()
is the most pythonic function to achieve just that:
def get_with_default(colour, colours, default):
search = (d for d in colours if d['color'] in (colour, default))
match_or_default = next(search)
if match_or_default['color'] != default or default == colour:
return match_or_default
return next(search, match_or_default)
next()
loops over the first argument until that produces a result, then returns that. If the first argument is exhausted, StopIteration
is raised instead, unless a second argument is given, a default, in which case that value is returned instead of raising the exception.
By giving it a generator expression that embodies the search, you efficiently scan the colours
list for the first match. If that turns out to be the default, then we continue to scan until we find the other match, or reach the end of the list.
Demo:
>>> get_with_default("blue", colours, "green")
{'color': 'blue', 'owner': 'Luke'}
>>> get_with_default("black", colours, "green")
{'color': 'green', 'owner': 'Mark'}
The above method is quite efficient in that it only has to scan the input list once, and the scan stops as soon as a match is found.
Note that this function will raise StopIteration
if the default is not present either:
>>> get_with_default("black", colours, "purple")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in get_with_default
StopIteration
You could instead return None
in that case by giving the first next()
call a default return value as well:
match_or_default = next(search, None)
Upvotes: 4
Reputation: 59984
Not the best, but nice and readable:
def get_with_default(colour, L, default=''):
temp = None
for d in L:
if d['color'] == colour:
return d
elif d['color'] == default:
temp = d
return temp
When testing:
>>> get_with_default('blue', colours, 'green')
{'color': 'blue', 'owner': 'Luke'}
>>> get_with_default('black', colours, 'green')
{'color': 'green', 'owner': 'Mark'}
Upvotes: 2