python中是否存在可以将不断增加的整数列表转换为范围列表的东西在Python中将整数列表转换为范围
例如,给定集合{0,1,2,3,4,7,8,9,11},我想得到{{0,4},{7,9},{11,11}}。
我可以写一个程序来做到这一点,但想知道是否有在python的内置功能
python中是否存在可以将不断增加的整数列表转换为范围列表的东西在Python中将整数列表转换为范围
例如,给定集合{0,1,2,3,4,7,8,9,11},我想得到{{0,4},{7,9},{11,11}}。
我可以写一个程序来做到这一点,但想知道是否有在python的内置功能
使用itertools.groupby产生一个简洁的,但棘手的实现:
import itertools
def ranges(i):
for a, b in itertools.groupby(enumerate(i), lambda (x, y): y - x):
b = list(b)
yield b[0][1], b[-1][1]
print list(ranges([0, 1, 2, 3, 4, 7, 8, 9, 11]))
输出:
[(0, 4), (7, 9), (11, 11)]
没有内置,或者在我所知道的任何库。我知道这不是很有帮助,但我从来没有遇到过任何你想要的东西。
以下是为您的程序ATLEAST的一些想法(在C++中,但它可以给你一些其他的想法):
在这种情况下在Python中没有这样的功能,这里是一个实现
p = []
last = -2
start = -1
for item in list:
if item != last+1:
if start != -1:
p.append([start, last])
start = item
last = item
p.append([start, last])
该发生器:
def ranges(p):
q = sorted(p)
i = 0
for j in xrange(1,len(q)):
if q[j] > 1+q[j-1]:
yield (q[i],q[j-1])
i = j
yield (q[i], q[-1])
sample = [0, 1, 2, 3, 4, 7, 8, 9, 11]
print list(ranges(sample))
print list(ranges(reversed(sample)))
print list(ranges([1]))
print list(ranges([2,3,4]))
print list(ranges([0,2,3,4]))
print list(ranges(5*[1]))
P roduces这些结果:反复数的运行
[(0, 4), (7, 9), (11, 11)]
[(0, 4), (7, 9), (11, 11)]
[(1, 1)]
[(2, 4)]
[(0, 0), (2, 4)]
[(1, 1)]
注意得到压缩。我不知道这是你想要的。如果没有,请将>
更改为!=
。
我理解你的问题。我看着itertools
,试图想出一个可以在几行Python中完成的解决方案,这个解决方案可以被认定为“几乎是内置的”,但我无法想出任何东西。
您可以使用list comprehension与generator expression和enumerate()和itertools.groupby()组合:
>>> import itertools
>>> l = [0, 1, 2, 3, 4, 7, 8, 9, 11]
>>> [[t[0][1], t[-1][1]] for t in
... (tuple(g[1]) for g in itertools.groupby(enumerate(l), lambda (i, x): i - x))]
[[0, 4], [7, 9], [11, 11]]
首先,enumerate()
将建立从列表项的元组和它们各自的索引:
>>> [t for t in enumerate(l)]
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 7), (6, 8), (7, 9), (8, 11)]
然后groupby()
意愿组使用它们的索引和它们的值(这将是连续值相等)之间的差的那些元组:
>>> [tuple(g[1]) for g in itertools.groupby(enumerate(l), lambda (i, x): i - x)]
[((0, 0), (1, 1), (2, 2), (3, 3), (4, 4)), ((5, 7), (6, 8), (7, 9)), ((8, 11),)]
从那里,我们只需要在每个组(这将是相同的,如果该组仅包含一个项目)的第一个和最后一个元组的值创建列表。
您还可以使用[(t[0][1], t[-1][1]) ...]
建设范围的元组,而不是嵌套列表,甚至((t[0][1], t[-1][1]) ...)
的列表,把整个表达式转换成可迭代generator
会懒洋洋地建立在动态范围的元组。
把它缩短:
ranges=lambda l:map(lambda x:(x[0][1],x[-1][1]),map(lambda (x,y):list(y),itertools.groupby(enumerate(l),lambda (x,y):x-y)))
生成范围对:
def ranges(lst):
s = e = None
r = []
for i in sorted(lst):
if s is None:
s = e = i
elif i == e or i == e + 1:
e = i
else:
r.append((s, e))
s = e = i
if s is not None:
r.append((s, e))
return r
实施例:
>>> lst = [1, 5, 6, 7, 12, 15, 16, 17, 18, 30]
>>> print repr(ranges(lst))
[(1, 1), (5, 7), (12, 12), (15, 18), (30, 30)]
作为发电机:
def gen_ranges(lst):
s = e = None
for i in sorted(lst):
if s is None:
s = e = i
elif i == e or i == e + 1:
e = i
else:
yield (s, e)
s = e = i
if s is not None:
yield (s, e)
实施例:
>>> lst = [1, 5, 6, 7, 12, 15, 16, 17, 18, 30]
>>> print repr(','.join(['%d' % s if s == e else '%d-%d' % (s, e) for (s, e) in gen_ranges(lst)]))
'1,5-7,12,15-18,30'
这是对非常优雅@juanchopanza answer的改进。这其中包括非唯一和非排序输入是python3兼容太:
import itertools
def to_ranges(iterable):
iterable = sorted(set(iterable))
for key, group in itertools.groupby(enumerate(iterable),
lambda t: t[1] - t[0]):
group = list(group)
yield group[0][1], group[-1][1]
例子:
>>> x
[44, 45, 2, 56, 23, 11, 3, 4, 7, 9, 1, 2, 2, 11, 12, 13, 45]
>>> print(list(to_ranges(x)))
[(1, 4), (7, 7), (9, 9), (11, 13), (23, 23), (44, 45), (56, 56)]
我觉得其他的答案是很难理解,并可能效率低下。希望这更容易,更快。
def ranges(ints):
ints = sorted(set(ints))
range_start = previous_number = ints[0]
for number in ints[1:]:
if number == previous_number + 1:
previous_number = number
else:
yield range_start, previous_number
range_start = previous_number = number
yield range_start, previous_number
几乎同样的问题被问和回答http://stackoverflow.com/questions/3429510/pythonic-way-to-convert-a-list-of-integers-into-a-string-of -comma-separated-range/3430231#3430231 – Apalala 2011-01-07 19:59:15
`>>> import this`` – Apalala 2011-01-08 20:55:35
嗯,我可以充满信心地说,我不知道这样的功能。这是很难说有信心,我不知道的东西不存在.... – 2011-01-07 17:31:09