5
\$\begingroup\$

I have a simple task that comes up often my work. I need to create a dictionary for an id key to a list of data associated with that key. I'm wondering if there is a better way to do this than the one I am using. The pattern is so common that I feel there must be better solution.

value_list_dict = {}

for line in f:
  line.strip()
  rec = line.split("\t")
  my_key = rec[0]
  important_value = rec[5] #or what ever it is

  # the repetitive pattern I find myself doing a lot of.
  if my_key not in value_list_dict:
    value_list_dict[my_key] = []
  value_list_dict[my_key].append(important_value)

While this solution is fairly concise and clear to me, I wonder if there is a better way.

I think that writing a custom function for this might be less readable although I'm open to suggestions.

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

line.strip() does not modify the variable, it returns a modified string. But you can chain it together with the split. I would also jchoose a better variable name than rec. As J.F.Sebastian suggested in the comments, you could go with items:

for line in f:
    items = line.strip().split('\t')

Your code is the perfect place for a collections.defaultdict. You can give it a type which it will use when the key is not defined. It makes implementing a counter a lot easier (just pass it int, whose default constructor returns an int with value 0) or, give it list and it will give you an empty list:

from collections import defaultdict

value_dict = defaultdict(list)

with open("file.txt") as f:
    for line in f:
        items = line.strip().split('\t')
        key, value = items[0], items[5]
        value_dict[key].append(value)

I also added the with..as construct in there, in case you are not yet using it.

In python 3.x I would use the extended iterable unpacking:

key, *other_vals, value = line.strip().split('\t')
\$\endgroup\$
6
  • \$\begingroup\$ line.strip() was a typo. I usally do line = line.strip(). I really like the defaultdict solution. I'm not sure what you mean by the advanced unpacking. Does that assume that value is the last item in the collection? \$\endgroup\$
    – pbible
    Aug 25, 2016 at 6:46
  • \$\begingroup\$ @pbible, yes it does. If there is more stuff coming afterwards, you will have to do it manually, two starred variables won't work. \$\endgroup\$
    – Graipher
    Aug 25, 2016 at 6:49
  • \$\begingroup\$ I could see how that could be useful. key, *other_vals, value = line.strip().split('\t')[:6] would work, but I'm getting off topic. Thanks, defaultdict is the perfect solution I think. \$\endgroup\$
    – pbible
    Aug 25, 2016 at 6:51
  • \$\begingroup\$ @pbible yes, that would work, but would defeat some point of having introduced that syntax in the first place (which was making first, rest = l[0], l[1:] easier and more efficient), as described in the linked PEP 3132 :) \$\endgroup\$
    – Graipher
    Aug 25, 2016 at 6:56
  • 1
    \$\begingroup\$ Please, do not use line name for the result of the split() that is a list (line is a string in your code i.e., line = line.strip() is ok). One name—one purpose (in the same context). You could use items, fields names instead. @pbible you could use key, value = getitems(line.strip().split('\t')) where getitems = operator.itemgetter(0,5) \$\endgroup\$
    – jfs
    Aug 25, 2016 at 7:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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