Skip to content

Instantly share code, notes, and snippets.

@kezabelle
Last active March 9, 2016 12:56
Show Gist options
  • Save kezabelle/395e56aa70655cd0c535 to your computer and use it in GitHub Desktop.
Save kezabelle/395e56aa70655cd0c535 to your computer and use it in GitHub Desktop.
Generate contiguous groups of integers in python. Mostly for generating range/between + exacts in Django/Haystack where parameters may have a max count (sqlite, lucene I'm looking at you!)
import itertools
import operator
def contiguous(unsorted):
def grouper(value):
return value[0] - value[1]
data = sorted(unsorted)
for k, g in itertools.groupby(enumerate(data), grouper):
yield map(operator.itemgetter(1), g)
data = [4,5,6,1,10,18,22,25,26,15,16,17,27,28,29,30,31,42,52,53,54]
output = tuple(contiguous(data))
print(output)
# ([1], [4, 5, 6], [10], [15, 16, 17, 18], [22], [25, 26, 27, 28, 29, 30, 31], [42], [52, 53, 54])
def make_query(results, key):
saved = set()
for result in results:
length = len(result)
if length == 1:
saved.add(result[0])
elif length > 1:
first = result[0]
last = result[-1]
yield {'%s__range' % key: (first, last)}
leftover_length = len(saved)
if leftover_length > 1:
yield {'%s__in' % key: saved}
elif leftover_length == 1:
yield {'%s__exact' % key: saved[0]}
newoutput = tuple(make_query(output, 'pk'))
print(newoutput)
# ({'pk__range': (4, 6)}, {'pk__range': (15, 18)}, {'pk__range': (25, 31)}, {'pk__range': (52, 54)}, {'pk__in': set([1, 10, 42, 22])})
def or_them(results, obj_cls):
return reduce(operator.or_, (obj_cls(**result) for result in results))
# import be_contiguous ...
from django.conf import settings
settings.configure()
from django.db.models.query_utils import Q
print(or_them(newoutput, Q))
# (OR: ('pk__range', (4, 6)), ('pk__range', (15, 18)), ('pk__range', (25, 31)), ('pk__range', (52, 54)), ('pk__in', set([1, 10, 42, 22])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment