4

Imagine a string containing (comma) separated elements, e.g. a version string

version_str = "3,1,4,159"

which might contain one or more elements more or less:

version_str = "3,1,4"

or

version_str = "3,1,4,159,appendix"

And I want to separate these elements like this:

major, minor, patch, revision, appendix = version_str.split(',')

Then of course I get an ValueError because the number of extracted elements does not always match.

Is there a way to extend the result of split(), e.g. like this:

version_str.split(',', min_elements=5)

or

version_str.split(',').extend(5, default='')

?

Example:

>>> '3,1,4'.split(',', min_elements=5)
['3', '1', '4', '', '']

>>> '3,1,4,159,dev'.split(',', min_elements=5)
['3', '1', '4', '159', 'dev']

Of course I can add elements manually afterwards or read elements conditionally, but I'm interested in the pythonic one-liner.

Mike Müller
  • 82,630
  • 20
  • 166
  • 161
frans
  • 8,868
  • 11
  • 58
  • 132

4 Answers4

5

Using zip_longest in one line:

from itertools import zip_longest
major, minor, patch, revision, appendix = [x + y for x, y in zip_longest(
                                           version_str.split(','),  [''] * 5, fillvalue='')]

or:

split = version_str.split(',')
major, minor, patch, revision, appendix = split + [''] * (5 - len(split))
Mike Müller
  • 82,630
  • 20
  • 166
  • 161
  • 1
    I like your answer because it's a real one-liner and it shows that there seems to be no intuitive way to do this yet. – frans Feb 06 '18 at 13:08
1

To my knowledge, there is no such functionality out-of-the-box but you can write your own function that does the trick for you:

def split_with_min(str, min_r, delimiter=',', default='NA'):
    temp = str.split(delimiter)
    return temp + [default] * (min_r - len(temp))


print(split_with_min('1,2', 5, ',', 'NA'))          # -> ['1', '2', 'NA', 'NA', 'NA']
print(split_with_min('1,2,3,4,5,6', 5, ',', 'NA'))  # -> ['1', '2', '3', '4', '5', '6']

Now for the one-liner requirement, you could condense the above if you do not mind calling split() twice:

a, b, c, d, e, f = my_str.split(',') + ['NA'] * (6 - len(my_str.split(',')))
print(f)  # -> 'NA'                              ^ number of variables we are defining
Ma0
  • 15,057
  • 4
  • 35
  • 65
1
version_str = "3,1,4,159,appendix"

version_str_1 = "3,1,4"

version_str_2 = "3,1,4,159"    

from collections import namedtuple

version = namedtuple("version", "major minor patch revision appendix")

version.__new__.__defaults__ = (None,) * len(version._fields)

print(version(*version_str_2.split(',')))
>>>version(major='3', minor='1', patch='4', revision='159', appendix=None)

print(version(*version_str_1.split(',')))
>>>version(major='3', minor='1', patch='4', revision=None, appendix=None)

print(version(*version_str.split(',')))
>>>version(major='3', minor='1', patch='4', revision='159', appendix='appendix')

to access individual fields:

get_version = version(*version_str_2.split(','))
get_version.major # '3'
get_version.minor # '1'
get_version.patch # '4'
get_version.revision # '159'
get_version.appendix # None
Veera Balla Deva
  • 790
  • 6
  • 19
0

I would suggest you to write a small inline(Or a normal) function that parse the version string

>>> version_parser = lambda s: "{},{},{},{},{}".format(*s.split(',') + ([""] * (5-len(s.split(',')))))

Then invoke it like

>>> version_parser('3,1,4,159').split(',')
>>> ['3', '1', '4', '159', '']

>>> version_parser("3,1,4").split(',')
>>> ['3', '1', '4', '', '']

>>> version_parser("3,1,4,159,appendix").split(',')
>>> ['3', '1', '4', '159', 'appendix']
Sohaib Farooqi
  • 5,457
  • 4
  • 31
  • 43