I have this list of countries:
countries = ['Estonia', 'Finland', 'Sweden', 'Denmark', 'Norway', 'Iceland']
I need to resolve following exersice: Use reduce to concatenate all the countries and to produce this sentence: Estonia, Finland, Sweden, Denmark, Norway, and Iceland are north European countries
def sentece(pais,pais_next):
if pais_next=='Iceland':
return pais ' and ' pais_next ' are north European countries'
else: return pais ', ' pais_next
countries_reduce=reduce(sentece,countries)
print(countries_reduce)
The code run perfect, but if I want to do in general, How I know what is the last element?.
CodePudding user response:
The reduce
function doesn't have a way to tell it what to do about the last item, only what to do about the initialization.
There's two general ways to go about it:
- Just do simple concatenation with a comma and a space, but only on the first
n-1
items of the list, then manually append the correct format for the last item - Change the last item from
Iceland
toand Iceland are north European countries
, then do the concatenation for the full list.
CodePudding user response:
Figuring out which is the last element is a bad idea; any solution that would give you that information would be a royal hack.
Normally, you wouldn't use reduce
to solve this at all (repeated concatenation is a form of Schlemiel the Painter's Algorithm, involving O(n²)
work, where efficient algorithms can be O(n)
), so you'd just use ', '.join
, e.g.:
countries = ['Estonia', 'Finland', 'Sweden', 'Denmark', 'Norway', 'Iceland']
countries_str = f'{", ".join(countries[:-1])} and {countries[-1]} are north European countries'
or, premodifying countries[-1]
to reduce the complexity to the point where an f-string isn't necessary (assuming an Oxford comma is okay):
countries = ['Estonia', 'Finland', 'Sweden', 'Denmark', 'Norway', 'Iceland']
countries[-1] = 'and ' countries[-1] # Put the "and " prefix in front ahead of time
countries_str = ', '.join(countries) ' are north European countries'
where join
is used for the consistent join components, wrapped in an f-string that inserts the last item along with the rest of the formatting.
If you must use reduce
, you'd still want to handle the final element separately, either by processing it completely separately at the end, e.g.
from functools import reduce
countries = ['Estonia', 'Finland', 'Sweden', 'Denmark', 'Norway', 'Iceland']
countries_str = f'{reduce(lambda x, y: f"{x}, {y}", countries[:-1])} and {countries[-1]} are north European countries'
print(countries_str)
or by manually tweaking it ahead of time so it can be used in a consistent manner (assuming you're okay with the Oxford comma):
from functools import reduce
countries = ['Estonia', 'Finland', 'Sweden', 'Denmark', 'Norway', 'Iceland']
countries[-1] = 'and ' countries[-1] # Put the "and " prefix in front ahead of time
countries_str = f'{reduce(lambda x, y: f"{x}, {y}", countries)} are north European countries'
print(countries_str)
Again, reduce
is a bad solution to the problem; str.join
(using ', '.join
) is a O(n)
solution (on CPython, it pre-scans the items to join to compute the final length, then preallocates the complete final str
, and copies each input exactly once), where reduce
is O(n²)
(and unlike an actual loop using =
, it can't even benefit from the CPython reference interpreter's implementation detail that sometimes allows concatenation to mutate in-place, reducing the number of data copies).