Question

I have a string like this

PARAMS = 'TEST = xy; TEST2= klklk '

which I want to split twice, once at the ";" and second on the "=" and then put it in a dict.

I can do it with this line:

dict(item.split("=") for item in PARAMS.split(";"))

and get:

{' TEST2': ' klklk ', 'TEST ': ' xy'}

I would now also like to strip the key and value before putting them in the dict. Is there an elegant way to do it in one line in python?

Était-ce utile?

La solution 2

dict([i.strip() for i in item.split("=")] for item in PARAMS.split(";"))

This runs a lot faster than @aIKid's solution :)

PARAMS = 'TEST = xy; TEST2= klklk '
from timeit import timeit
print timeit('dict((i.strip() for i in item.split("=")) for item in PARAMS.split(";"))', "from __main__ import PARAMS")
print timeit('dict([i.strip() for i in item.split("=")] for item in PARAMS.split(";"))', "from __main__ import PARAMS")

Output

18.7284784281
9.16360774723

Autres conseils

I don't know exactly what you call 'elegant', but this works:

dict((i.strip() for i in item.split("=")) for item in PARAMS.split(";"))

Maybe something like:

dict(map(lambda x: x.strip(), item.split("=")) for item in PARAMS.split(";"))

or another even more elegant version:

dict((l[i].strip(), l[i+1].strip()) for i in range(2) for l in [re.split(';|=', PARAMS)])

Of course this is elegant only if you take it as a synonym of obfuscated, but when we are seeking one-liners is it not what we mean ?

To solve this problem I would probably write:

d = dict(); 
for item in PARAMS.split(";"):
    key, value = item.split("=")
    d[key.strip()] = value.strip()

It is both easier to read and faster than all the proposed answer until now, and I didn't even bothered to optimize it in any way, henceforth it is probably not the best possible solution.

Don't believe it on words, time the different solutions to check:

PARAMS = 'TEST = xy; TEST2= klklk '

from timeit import timeit

print 'obfuscated', timeit('dict((l[i].strip(), l[i+1].strip()) for i in range(2) for l in [re.split(";|=", PARAMS)])', "from __main__ import PARAMS; import re")
print 'tuple', timeit('dict((i.strip() for i in item.split("=")) for item in PARAMS.split(";"))', "from __main__ import PARAMS")
print 'regex', timeit('dict(re.findall(r"(\S+)\s*=\s*([^\s;]+)", PARAMS))', "from __main__ import PARAMS; import re")
print 'lambda', timeit('dict(map(lambda x: x.strip(), item.split("=")) for item in PARAMS.split(";"))', "from __main__ import PARAMS; import re")
print 'list comprehension', timeit('dict([i.strip() for i in item.split("=")] for item in PARAMS.split(";"))', "from __main__ import PARAMS")
print 'replace spaces', timeit('dict(item.split("=") for item in PARAMS.replace(" ", "").split(";"))', "from __main__ import PARAMS; import re")

print 'not one line', timeit(
'''
    d = dict(); 
    for item in PARAMS.split(";"):
        key, value = item.split("=")
        d[key.strip()] = value.strip()
    d
''',
"from __main__ import PARAMS")

Below are the timing results:

  • obfuscated: 7.36826086044
  • tuple: 4.49374079704
  • regex: 3.61684799194
  • lambda: 3.51627087593
  • list comprehension: 2.90777206421
  • replace spaces: 2.46001887321
  • not one line: 1.71015286446

It speaks for itself.

PS: the reason why the not one line is faster is probably because it avoids creating an unecessary list structure, but directly store value in the dict. But that was a no brainer, not even voluntary.

Or, alternatively:

import re
text = 'TEST = xy; TEST2= klklk '
params = dict(re.findall(r'(\S+)\s*=\s*([^\s;]+)', text))
# {'TEST': 'xy', 'TEST2': 'klklk'}

If none of your keys or values have spaces inside them, then you're free to eliminate all spaces with a single replace method:

>>> dict(item.split("=") for item in PARAMS.replace(" ", "").split(";"))
{'TEST': 'xy', 'TEST2': 'klklk'}

This will eliminate more spaces than strip would, of course:

>>> PARAMS = 'TEST 3 = there should be spaces between these words '
>>> dict(item.split("=") for item in PARAMS.replace(" ", "").split(";"))
{'TEST3': 'thereshouldbespacesbetweenthesewords'}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top