0

I have a dictionary "metadata" that contains key-value pairs of strings, lists, dicts, and integers. How can I loop through this dictionary and convert all values that are not of type string or integer to string?

Is there any way to simultaneously remove keys for which the value is empty? How could I do this without adding an additional loop?

ensnare
  • 40,069
  • 64
  • 158
  • 224
  • 2
    The second part of your question is the same as what you asked on [an earlier question](http://stackoverflow.com/questions/12118695/efficient-way-to-remove-keys-with-empty-values-from-a-dict/12118700#12118700). As for the rest, what have you tried? The answer to that earlier question should give you a good start on the general task of fiddling with dict contents. – BrenBarn Aug 25 '12 at 21:34
  • Or more generally, any type that's not a string or integer should be converted to string. – ensnare Aug 25 '12 at 21:35
  • @BrenBarn: I'm not sure how to detect types that are not integer or string. I'm also not sure how this integrates into the one-liner from the previous answer. Right now I'm doing 2 iterations (inefficient) and am looking for specific data types (list + dict) but I'd rather find a more general solution that's everything except int + string. – ensnare Aug 25 '12 at 21:39
  • 2
    Sounds like the key thing to learn about is the [`isinstance`](http://docs.python.org/library/functions.html#isinstance) function. – Blckknght Aug 25 '12 at 21:42

2 Answers2

3

Use isinstance to check the type of a value. In Python 3, you can simply write:

{k:v if isinstance(v, (str, int)) else str(v) for k,v in dct.items() if k != ''}

In older Python versions, you must be aware that there are no dictionary comprehensions (<2.7), and that there are multiple types that could be built-in ints and strings(<3). If you mean character strings when you talk about strings, the 2.5-2.7 equivalent would be

dict((k, v if isinstance(v, (unicode, int, long)) else unicode(v))
     for k,v in dct.iteritems())
phihag
  • 278,196
  • 72
  • 453
  • 469
  • 1
    Great solution (+1) but curious: why use `isinstance` vs `type`? – the wolf Aug 25 '12 at 22:08
  • 2
    Because `isinstance` will match subclasses. For example, if someone chooses to implement `OptimizedString` with a string that's faster than Python's built-in one (say, because they always know the encoding in their application, or the string is connected to network processing, or shared with TCL, etc.), this will still work. [numpy's integer types](http://docs.scipy.org/doc/numpy/user/basics.types.html) are still valid `int`s. Checking with `type(v) in ...` would miss these subclasses. – phihag Aug 25 '12 at 22:31
1

This works:

d={'l':[1,2,3],'s':'abc','i':5,'f':1.23,'di':{1:'one',2:'two'},'emty':''}
{k:v if type(v) in (str,int, long,unicode) else repr(v) for k,v in d.items() if v!=''} 

Prints:

{'i': 5, 's': 'abc', 'di': "{1: 'one', 2: 'two'}", 'l': '[1, 2, 3]', 'f': '1.23'}
the wolf
  • 34,510
  • 13
  • 53
  • 71
  • 1
    ? This converts everything to its `repr`, which won't leave `int`s alone, and adds an extra quote to the strings (e.g. `"abc"` becomes `"'abc'"`). And `if v` will remove any 0 integers. – DSM Aug 25 '12 at 21:52