python:[[1,2],[3,4],[5,6],[7,8]] 转换为 [[1],[2,3],[4,5],[6,7],[8]] 及其反向转换
我现在的解决方案有两个思路:
- 通过一个迭代器类来生成新的组合后的内部列表
- 或者通过一个迭代函数来生成新的组合后的内部列表
有没有其他更好的方法来解决这个问题呢?
编辑
@Glenn:你的意见很好。我之前没有考虑到这一点,因为我发现列表的顺序并不是我想的那样。
@THC4k:谢谢你的解决方案。我学到了chain.from_iterable这个方法。
@Mike DeSimone:嗯,我测试了你的解决方案,但似乎出了点问题,可能我还漏掉了什么……
@Jamie和Odomontois:谢谢你们提醒我需要更详细一些。
我的目标
我正在制作一个小算法,它可以把一个任务列表——成对的元组(开始,结束)——转换成一个简化的任务列表,把重叠的任务合并在一起。
有一个例外:当一个事件完全被另一个事件重叠时,我的算法会失败(s1 s2 e2 e1)。
详细说明:
- 我有一个名为'taskList'的列表,里面是成对的元组(经验教训 - 元组 :))。
- 每个元组包含两个日期时间对象:任务的开始和结束时间。
- 重要的是'taskList'的顺序是根据开始时间来决定的,因为任务可能会重叠。
- 'taskList'包含多个日期,因此使用日期时间对象。
举个例子,为了可读性,这里只是时间的字符串表示:
taskList = [(9:00,10:00),(9:30,11:00),(11:00,12:30),(13:30,14:00),(14:00,18:00)]
最终结果:
result = [(9:00,12:30), (13:30,18:00)]
我现在的想法是,当我以我提问的方式重新排列'taskList'时……
taskListT1 = [(9:00,),(10:00,9:30),(11:00,11:00),(12:30,13:30),(14:00,14:00),(18:00,)]
现在我可以去掉那些元组(a,b),其中a >= b:
taskListT2 = [(9:00,),(12:30,13:30),(18:00,)]
然后再转换回来:
result = [(9:00,12:30), (13:30,18:00)]
6 个回答
0
你是指:
pairs = [[1,2], [3,4], [5,6], [7,8]]
print pairs, '->',
transformed = ([[pairs[0][0]]]+
[[a,b] for a,b in zip(
(second for first, second in pairs[:-1]),
(first for first, second in pairs[1:]))]+
[[pairs[-1][-1]]]
)
print transformed
""" Output:
[[1, 2], [3, 4], [5, 6], [7, 8]] -> [[1], [2, 3], [4, 5], [6, 7], [8]]
"""
1
选择“另一种更好的方法”这个选项(甚至正确处理了提问者的例外情况):
def compress_task_list(tasks):
tasks = list(tasks)
tasks.sort(key=lambda item: item[0]) # make sure list is in order by start time
result = []
first_start = tasks[0][0]
final_stop = tasks[0][1]
for start, stop in tasks[1:]:
if start > final_stop:
result.append((first_start, final_stop))
first_start = start
final_stop = stop
elif stop > final_stop:
final_stop = stop
result.append((first_start, final_stop))
return tuple(result)
if __name__ == '__main__':
import unittest
class Test_Compress_Task_List(unittest.TestCase):
def test_01(self):
"completely separate"
initial = ((8.0, 9.5), (10.0, 12.0), (13.0, 15.5), (16.0, 17.0))
expected = ((8.0, 9.5), (10.0, 12.0), (13.0, 15.5), (16.0, 17.0))
self.assertEqual(compress_task_list(initial), expected)
def test_02(self):
"end equals start"
initial = ((8.0, 9.5), (9.5, 12.0), (13.0, 15.5), (15.5, 17.0))
expected = ((8.0, 12.0), (13.0, 17.0))
self.assertEqual(compress_task_list(initial), expected)
def test_03(self):
"end equals start (with more empty times)"
initial = ((8.0, 8.5), (8.5, 10.0), (10.25, 12.0), (12.5, 13.75), (13.75, 15.0), (15.25, 16.0), (16.0, 17.0))
expected = ((8.0, 10.0), (10.25, 12.0), (12.5, 15.0), (15.25, 17.0))
self.assertEqual(compress_task_list(initial), expected)
def test_04(self):
"out of order, cross-overs, and tasks completely inside other tasks"
initial = ((8.0, 8.5), (8.0, 10.0), (10.25, 12.0), (10.0, 11.5), (13.0, 15.5), (14.0, 15.0), (16.0, 17.0))
expected = ((8.0, 12.0), (13.0, 15.5), (16.0, 17.0))
self.assertEqual(compress_task_list(initial), expected)
unittest.main()
记住,这里是Python,代码的可读性很重要哦。 ;)
1
好的,这里是使用yield的解决方案:
# transform forwards
def transform_pairs( lst ):
it = iter(lst)
a,last = next(it)
yield [a]
for a,b in it:
yield last, a
last = b
yield [last]
把列表转换回来应该看起来很相似,不过我就不多说了,留给读者自己去做。
这里还有一个稍微复杂一点的例子,它可以双向转换。它返回的是元组,因为固定长度的列表有点无趣。
from itertools import chain
def transform( iterable, offset):
it = chain.from_iterable(iterable) # turn it back to one long list.
if offset:
yield next(it), # the trailing `,` makes this a tuple.
for item in it:
try:
x = next(it)
except StopIteration: # there is no 2nd item left
yield item,
else:
yield item, x # yield the pair
print list(transform(transform([[1,2],[3,4],[5,6],[7,8]], True), False))