Reputation: 5566
I have a list like this:
[u'1.9', u'comment', u'1.11', u'1.5', u'another comment']
I want to split it into tuples such that number strings (for which isdigit(item[0])
is True
) are paired with either the comment that comes immediately after them, or with an empty string if there is no comment (i.e., next item is another number string).
In other words:
[
(u'1.9', u'comment'),
(u'1.11', ''),
(u'1.5', u'another comment'),
]
What is the cleanest way to go about this, especially since the list could be odd or even in length?
Upvotes: 1
Views: 231
Reputation: 2724
I'd go with something like this (pairwise
comes from itertools
cookbook):
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')]
It should work both for single element in input
and for input
ending with version string.
Upvotes: 0
Reputation: 1239
Firstly, "isdigit" can only seem test integer, so for simplicity I firstly assume the input is:
>>> lst = [u'9', u'comment', u'11', u'1001', u'another comment']
Then the solution is one liner:
>>> [(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')]
Explanation
zip generates all the pairs,
>>> lst = ['a', 'b', 'c', 'd']
>>> zip(lst[:-1], lst[1:]
[('a', 'b'), ('b', 'c'), ('c', 'd')]
Then use a following if statement to filter the pairs starts with an integer string.
>>> [(i, s) for i, s in zip(...) if i.isdigit()]
Finally, use if else statement to generate result, which is either (i, '') or (i, s)
BTW, this doesn't work when there's only one element in the lst. In this case you should handle specifically.
Upvotes: 0
Reputation: 955
Here is how to do it in a single list comprehension.
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()]
Upvotes: 1
Reputation: 1122082
Your best bet is to use a generator function to do the pairing:
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)
You can then just iterate over the generator or produce a list from it with:
result = list(number_paired(items))
This also handles the case where you have a number at the end and no following comment:
>>> 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'')]
Upvotes: 2