mnowotka
mnowotka

Reputation: 17258

How to simplify repetitive list comprehensions in python?

This code computes smallest rectangle containing a list of input rectangles:

left = min(x.left for x in rect)
bottom = min(x.bottom for x in rect)
right = max(x.right for x in rect)
top = max(x.top for x in rect)

I'm sure this can be simplified to single line statement but honestly I don't know how. Any help would be appreciated.

Upvotes: 0

Views: 269

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1124788

Don't use a generator expression here, just loop once, updating minimi and maximi in one go:

left = bottom = float('inf')
right = top = float('-inf')
for x in rect:
    if x.left < left:     left = x.left
    if x.bottom < bottom: bottom = x.bottom
    if x.right > right:   right = x.right
    if x.top > top:       top = x.top

Yes, this is more verbose, but also more efficient. The longer it is, the less work the above loop performs compared to your 4 generator expressions.

You can produce dictionaries of results:

minimi = dict.fromkeys(('left', 'bottom'), float('inf'))
maximi = dict.fromkeys(('right', 'top'), float('-inf'))
for x in rect:
    for key in minimi:
        if getattr(x, key) < minimi[key]: minimi[key] = getattr(x, key)
    for key in maximi:
        if getattr(x, key) > maximi[key]: maximi[key] = getattr(x, key)

but that's hardly worth the abstraction, not for 2 values each.

Upvotes: 1

superjump
superjump

Reputation: 151

You can solve the problem with a reduction, but it's probably less efficient than Martijn Pieters suggestion.

from functools import reduce

def fn(current_mins_maxs, r):
    z = zip(current_mins_maxs, (r.left, r.bottom, r.right, r.top))
    return [min(z[0]), min(z[1]), max(z[2]), max(z[3])]

first, rest = rectangles[0], rectangles[1:]
# Or, if you are using Python 3, you can write:
# first, *rest = rectangles
mins_and_maxs = reduce(fn, rest, (first.left, first.bottom, first.right, first.top))    

Upvotes: 0

Related Questions