4
\$\begingroup\$

I am trying to convert "Random = 1" into dictionary {"Random":1} I tried

Key = (line.split('=')[0]).split()[0] #to get key and remove spaces
value = (line.split('=')[1]).split()[0]
CQ_Options[Key] = value

How could I do it in more mature way?

\$\endgroup\$
4
  • 4
    \$\begingroup\$ Why do you want to do this? Because, if you want to parse name-value contained files, I prefer the ConfigParser lib \$\endgroup\$
    – andras.tim
    Apr 10 '17 at 12:23
  • \$\begingroup\$ @andras.tim is right. In cases like this where the input is presumed from an uncontrolled outside source (file) in a non-strict format (optional spaces surrounding =), it is better to use a standard lib. Other benefits in this case, include ignoring comment lines. The only time home-grown one-offs are both safe & easy is when the input is also controlled by you (i.e. a companion microservice passing URL args.) \$\endgroup\$
    – MarkHu
    Apr 10 '17 at 17:02
  • \$\begingroup\$ Agree with @andras.tim. If at all possible, use a standard format with existing parsers, e.g. INI, JSON, YAML, even XML is better than custom. In the event that using preexisting formats isn't an option for you, consider pyparsing. \$\endgroup\$
    – jpmc26
    Apr 11 '17 at 0:40
  • \$\begingroup\$ @andras.tim I am quite new to python and exploring utilities of it so I thought of not using existing libraries. \$\endgroup\$
    – NGB
    Apr 11 '17 at 6:25
10
\$\begingroup\$

First off, you should standardize your names, Key and value don't follow the same naming rules, or if they do, they're not apparent to me. And so I'd recommend that you name your things in lower_snake_case, to adhere to PEP8.

After this I'd use str.strip rather than str.split. And use a list comprehension to change both the key and value without having to duplicate the code, which can remove the key and value variables altogether. This can leave you with:

item = [i.strip() for i in line.split('=')]
CQ_options[item[0]] = item[1]

If you want to stick with using key and value however, you need to make sure that line.split('=') will only return two items. You can make sure it'll return a max of two items with the optional parameter, and since we'd already error if there's not at least a single =, then you can use:

key, value = [i.strip() for i in line.split('=', 1)]
CQ_options[key] = value
\$\endgroup\$
7
\$\begingroup\$

You can use a dict comprehension if you have multiple items in a list, for example:

a = ["Random = 1", "Ran = 3"]
print {k.strip(): int(v.strip()) for k, v in (i.split("=") for i in a)}

>>> {'Ran': 3, 'Random': 1}

If you want to update an existing dictionary d:

d.update({k.strip(): int(v.strip()) for k, v in (i.split("=") for i in a)})
\$\endgroup\$
3
  • \$\begingroup\$ Sorry I changed my answer completely, the first one was no very helpful! \$\endgroup\$
    – kezzos
    Apr 10 '17 at 12:51
  • \$\begingroup\$ This answer shows how to use a dict comprehension \$\endgroup\$
    – kezzos
    Apr 10 '17 at 15:08
  • \$\begingroup\$ print {k.strip(): int(v.strip()) for k, v in [line.split("=")]} would be the way to split the OP one-line data source. But that's only a few characters shorter than just explicitly doing the also-confusing print {line.split("=")[0].strip() : int(line.split("=")[-1].strip())} --all that to say, I don't think dict comprehension is a good fit here. \$\endgroup\$
    – MarkHu
    Apr 10 '17 at 22:19
4
\$\begingroup\$

Another naive approach would be to take advantage of the fact that default split() uses whitespace, and discard the = delimiter.

line = "Random = 1"
my_dict = { line.split()[0] : line.split()[2]}

Note that the un-used value of line.split()[1] is = in this case, but could be : or anything. But this is naive in that it requires spaces.

A little better:

import re
line = "Random= 1"
my_dict = { re.split(' |=',line)[0] : re.split(' |=',line)[-1] }

Both examples yield {'Random': '1'} but the latter is more flexible. But better yet, try a standard lib like configparser which has other nice features like ignoring comment lines.

\$\endgroup\$
1
\$\begingroup\$

If you have one key-value pair per line, and you just want to parse all lines and transform them into a dictionary:

import re
...
CQ_Options = dict(re.split(r'\s*=\s*', line, 1) for line in text.splitlines())

I don't know if you're familiar with regular expressions or Python in general so I'll explain.

This will:

  • First split text into lines and remove newline characters. The splitlines method will take care of \r aka CR on Windows.
  • Then each line is split by the regular expression \s*=\s*:
    • \s is whitespace, such as spaces and tabs
    • * is zero or more repetitions of the previous expression, in this case zero or more whitespace characters
    • the argument 1 means it will only split at the first occurrence, not all occurrences
    • so the line is split by the first equals sign preceded and followed by an arbitrary amount of spaces, tabs, etc.
  • Then you have a sequence of key-value pairs.
  • These will be converted into a dictionary using the dict constructor, which given a sequence of key-value pairs converts them into a dictionary.

EDIT

@Peilonrayz has pointed out that this will not strip whitespace on the outer side of the key-value pair. For example " key = value " will become {' key': 'value '}. I originally assumed whitespace would be only between the key, equals sign, and value.

To remove whitespace on both sides of key and value:

CQ_Options = dict(
  map(str.strip, line.split('=', maxsplit=1))
  for line in text.splitlines()
)
\$\endgroup\$
6
  • \$\begingroup\$ This doesn't remove white space around each key and value. ' abc = def ' will become {' abc': 'def '}. Also obligatory XKCD. \$\endgroup\$
    – Peilonrayz
    Apr 10 '17 at 10:35
  • \$\begingroup\$ Come on. Don't tell me you don't ever find RE useful. You're right about the whitespace but guess what, it can be solved by using regular expressions. Actually the whole thing can be done using a single finditer. \$\endgroup\$
    – kyrill
    Apr 10 '17 at 10:38
  • 1
    \$\begingroup\$ Yes I do find them useful, here maybe they're overkill, and over-reliance leads to problems. \$\endgroup\$
    – Peilonrayz
    Apr 10 '17 at 10:41
  • 1
    \$\begingroup\$ @Peilonrayz Well, there's another XKCD :-P \$\endgroup\$
    – ChatterOne
    Apr 10 '17 at 10:54
  • 1
    \$\begingroup\$ * actually means 0 or more, + means 1 or more repetitions. \$\endgroup\$ Apr 10 '17 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.