有条件地在python中配对列表项

时间:2015-08-21 18:13:51

标签: python list for-loop list-comprehension

我有一个这样的清单:

[u'1.9', u'comment', u'1.11', u'1.5', u'another comment']

我想将它拆分为元组,使得数字字符串(isdigit(item[0])True)与紧跟在它们之后的注释配对,或者如果没有则与空字符串配对注释(即,下一个项目是另一个数字字符串)。

换句话说:

[
  (u'1.9', u'comment'),
  (u'1.11', ''),
  (u'1.5', u'another comment'),
]

最简洁的方法是什么,特别是因为列表可能是奇数甚至是长度?

4 个答案:

答案 0 :(得分:2)

您最好的选择是使用生成器功能进行配对:

def number_paired(items):
    items = iter(items)
    number = next(items)
    while number is not None:
        comment = next(items, None)
        if comment is None or comment[0].isdigit():
            # really a number, or end of the iterable
            yield number, u''
            number = comment
            continue
        yield number, comment
        number = next(items)

然后您可以迭代生成器或从中生成一个列表:

result = list(number_paired(items))

这也处理了最后有一个数字并且没有以下注释的情况:

>>> def number_paired(items):
...     items = iter(items)
...     number = next(items)
...     while number is not None:
...         comment = next(items, None)
...         if comment is None or comment[0].isdigit():
...             # really a number, or end of the iterable
...             yield number, u''
...             number = comment
...             continue
...         yield number, comment
...         number = next(items)
... 
>>> items = [u'1.9', u'comment', u'1.11', u'1.5', u'another comment']
>>> list(number_paired(items))
[(u'1.9', u'comment'), (u'1.11', u''), (u'1.5', u'another comment')]
>>> list(number_paired(items + [u'42']))
[(u'1.9', u'comment'), (u'1.11', u''), (u'1.5', u'another comment'), (u'42', u'')]

答案 1 :(得分:1)

以下是如何在单个列表理解中完成它。

my_list = [u'1.9', u'comment', u'1.11', u'1.5', u'another comment']

result = [(x,('' if i + 1 >= len(my_list) or my_list[i + 1].replace('.','').isdigit() else my_list[i + 1])) for i, x in enumerate(my_list) if x and x.replace('.','').isdigit()]

答案 2 :(得分:0)

我会这样做(pairwise来自itertools食谱):

input = [u'1.9', u'comment', u'1.11', u'1.5', u'another comment']

from itertools import tee, izip
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

import re
v_re = re.compile('\d\.\d+')

[(a, b) if not v_re.match(b) else (a, '') for a, b in pairwise(input + [u'']) if v_re.match(a)]
# [(u'1.9', u'comment'), (u'1.11', ''), (u'1.5', u'another comment')]

它应该适用于input中的单个元素和以版本字符串结尾的input

答案 3 :(得分:0)

首先,“isdigit”只能看似测试整数,所以为简单起见我首先假设输入是:

>>> lst = [u'9', u'comment', u'11', u'1001', u'another comment']

然后解决方案是一个班轮:

>>> [(i, '') if s.isdigit() else (i, s) for i, s in zip(lst[:-1], lst[1:]) if i.isdigit()]
[(u'9', u'comment'), (u'11', ''), (u'1001', u'another comment')]

<强>解释

zip生成所有对,

>>> lst = ['a', 'b', 'c', 'd'] 
>>> zip(lst[:-1], lst[1:]
[('a', 'b'), ('b', 'c'), ('c', 'd')]

然后使用以下if语句以整数字符串过滤对。

>>> [(i, s) for i, s in zip(...) if i.isdigit()]

最后,使用if else语句生成结果,即(i,'')或(i,s)

顺便说一句,当lst中只有一个元素时,这不起作用。在这种情况下,你应该专门处理。