将整数列表转换为逗号分隔范围的字符串的Pythonic方法 [英] Pythonic way to convert a list of integers into a string of comma-separated ranges

查看:103
本文介绍了将整数列表转换为逗号分隔范围的字符串的Pythonic方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个整数列表,需要将其解析为一系列字符串.

I have a list of integers which I need to parse into a string of ranges.

例如:

 [0, 1, 2, 3] -> "0-3"
 [0, 1, 2, 4, 8] -> "0-2,4,8"

以此类推.

我仍在学习处理列表的更多pythonic方式,这对我来说有点困难.我最近的想法是创建一个列表列表,以跟踪成对的数字:

I'm still learning more pythonic ways of handling lists, and this one is a bit difficult for me. My latest thought was to create a list of lists which keeps track of paired numbers:

[ [0, 3], [4, 4], [5, 9], [20, 20] ]

然后我可以遍历此结构,将每个子列表打印为一个范围或单个值.

I could then iterate across this structure, printing each sub-list as either a range, or a single value.

我不喜欢在两次迭代中执行此操作,但是我似乎无法跟踪每次迭代中的每个数字.我的想法是要做这样的事情:

I don't like doing this in two iterations, but I can't seem to keep track of each number within each iteration. My thought would be to do something like this:

这是我最近的尝试.它可以工作,但是我并不完全满意.我一直在想,还有一个更优雅的解决方案使我完全逃脱了.我知道,字符串处理迭代并不是最好的方法-对我来说这是一大清早:)

Here's my most recent attempt. It works, but I'm not fully satisfied; I keep thinking there's a more elegant solution which completely escapes me. The string-handling iteration isn't the nicest, I know -- it's pretty early in the morning for me :)

def createRangeString(zones):
        rangeIdx = 0
        ranges   = [[zones[0], zones[0]]]
        for zone in list(zones):
            if ranges[rangeIdx][1] in (zone, zone-1):
                ranges[rangeIdx][1] = zone
            else:
                ranges.append([zone, zone])
                rangeIdx += 1

        rangeStr = ""
        for range in ranges:
            if range[0] != range[1]:
                rangeStr = "%s,%d-%d" % (rangeStr, range[0], range[1])
            else:
                rangeStr = "%s,%d" % (rangeStr, range[0])

        return rangeStr[1:]

有没有直接的方法可以将其合并为单个迭代?我还可以做些什么来使其更像Pythonic?

Is there a straightforward way I can merge this into a single iteration? What else could I do to make it more Pythonic?

推荐答案

>>> from itertools import count, groupby
>>> L=[1, 2, 3, 4, 6, 7, 8, 9, 12, 13, 19, 20, 22, 23, 40, 44]
>>> G=(list(x) for _,x in groupby(L, lambda x,c=count(): next(c)-x))
>>> print ",".join("-".join(map(str,(g[0],g[-1])[:len(g)])) for g in G)
1-4,6-9,12-13,19-20,22-23,40,44

这里的想法是将每个元素与count()配对.然后,对于连续的值,该值与count()之间的差是恒定的. groupby()完成其余工作

The idea here is to pair each element with count(). Then the difference between the value and count() is constant for consecutive values. groupby() does the rest of the work

正如Jeff所建议的,count()的替代方法是使用enumerate().这增加了一些多余的东西,需要在打印语句中删除

As Jeff suggests, an alternative to count() is to use enumerate(). This adds some extra cruft that needs to be stripped out in the print statement

G=(list(x) for _,x in groupby(enumerate(L), lambda (i,x):i-x))
print ",".join("-".join(map(str,(g[0][1],g[-1][1])[:len(g)])) for g in G)

更新:对于此处给出的示例列表,带有枚举的版本的运行速度比我计算机上使用count()的版本慢5%.

Update: for the sample list given here, the version with enumerate runs about 5% slower than the version using count() on my computer

这篇关于将整数列表转换为逗号分隔范围的字符串的Pythonic方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆