Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
447 views
in Technique[技术] by (71.8m points)

python - Pythonic way to convert a list of integers into a string of comma-separated ranges

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

For example:

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

And so on.

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:]

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
>>> 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

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

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)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...