Bored Trevor
Bored Trevor

Reputation: 45

Pythonic List Iteration

I've got a list of Django Querysets that I'd like to combine into a single Query. The standard way of merging two Querysets is to perform the operation: newQ = Q1 | Q2, and I'd like to perform that operation on all elements of my list to form a single Queryset object.

This is pretty straightforward to do using a for loop, eg:

for qs in qs_list:
    if final_qs not in locals():
        final_qs = qs
    else:
        final_qs = final_qs | qs  

Given the wonders of python it feels as though there is probably an inbuilt function of some kind that will do this for you. However, I had a look through the itertools library and nothing jumped out as a way of simplifying this operation.

So my question is, is there a more pythonic way of performing the above operation?

Upvotes: 1

Views: 133

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1124988

Yes, that function is called functools.reduce(). Use it with operator.or_():

import operator
from functools import reduce

final_qs = reduce(operator.or_, qs_list)

reduce() takes the first to values of qs_list, passes those to the first argument, operator.or_, effectively executing qs_list[0] | qs_list[1]. It then takes that result, plus the next value in qs_list and applies the first argument again, and so on until qs_list is done.

For qs_list with 4 elements, that comes down to:

or_(or_(or_(qs_list[0], qs_list[1]), qs_list[2]), qs_list[3])

or the equivalent of:

qs_list[0] | qs_list[1] | qs_list[2] | qs_list[3]

but reduce works for any length of qs_list > 0 (for a list of length 1 the first value is returned without applying the first argument).

Note that your use of if final_qs not in locals() is very unpythonic; don't ever do that. You could have written your loop with an initial final_qs from qs_list[0] instead:

final_qs = qs_list[0]
for qs in qs_list[1:]:
    final_qs |= qs

Upvotes: 5

Related Questions