python:[[1,2],[3,4],[5,6],[7,8]] 转换为 [[1],[2,3],[4,5],[6,7],[8]] 及其反向转换

3 投票
6 回答
891 浏览
提问于 2025-04-16 02:52

我现在的解决方案有两个思路:

  • 通过一个迭代器类来生成新的组合后的内部列表
  • 或者通过一个迭代函数来生成新的组合后的内部列表

有没有其他更好的方法来解决这个问题呢?

编辑

@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))

撰写回答