Peter Moore
Peter Moore

Reputation: 2086

stumped getting key value from a comprehension

Im trying to figure out how to get values from comprehension that is almost working. From this data ..

{'rock': {}, 'coal1': {'gold1': {'data': ['g1']}}, 'coal2': {'gold3': {'data': ['g3']}, 'gold2': {'data': ['g2']}}}

.. im trying to extract the gold key data into simple dict. i.e a dict of { gold : {'data' : [...]} } items essentially ripping out the coal keys from the data.

in other words from this ..

{
  "coal2": {
    "gold3": {
      "data": [
        "g3"
      ]
    },
    "gold2": {
      "data": [
        "g2"
      ]
    }
  },
  "coal1": {
    "gold1": {
      "data": [
        "g1"
      ]
    }
  },
  "rock": {}
}

to this format

{
  "gold3": {
    "data": [
      "g3"
    ]
  },
  "gold1": {
    "data": [
      "g1"
    ]
  },
  "gold2": {
    "data": [
      "g2"
    ]
  }
}

That is almost working. This gets rid of the rock.

>>> {k:d for k,d in data.items() if k != 'rock'}
{'coal2': {'gold3': {'data': ['g3']}, 'gold2': {'data': ['g2']}}, 'coal1': {'gold1': {'data': ['g1']}}}

And getting the values gets rid of the coal keys.

>>> [v for v in {k:d for k,d in data.items() if k != 'rock'}.values()]
[{'gold3': {'data': ['g3']}, 'gold2': {'data': ['g2']}}, {'gold1': {'data': ['g1']}}]

But i cant figure out how to get from this

>>> for i in [v for v in {k:d for k,d in data.items() if k != 'rock'}.values()] : print(i)
...
{'gold3': {'data': ['g3']}, 'gold2': {'data': ['g2']}}
{'gold1': {'data': ['g1']}}

to the desired structure. And it would be sweet if it could all be done with comprehensions. Does anyone know how to accomplish this ?

EDIT: Both answers was amazing and I wish i could accept both. I like not importing anything but I accept @blhsing itertools version just because it is easier to comprehend and its a fraction better performance. BTW rock must be discarded even it has values so i could not bypass if k != 'rock'. So here is the results and... Thanks guys.

>>> import timeit
>>> data = {'rock': {'type':'pebble'}, 'coal1': {'gold1': {'data': ['g1']}}, 'coal2': {'gold3': {'data': ['g3']}, 'gold2': {'data': ['g2']}}}
>>> timeit.timeit( "dict(kv for x in (v for v in {k:d for k,d in data.items() if k != 'rock'}.values()) for kv in x.items())" ,  setup="from __main__ import data")            
2.6714617270044982
>>>
>>> timeit.timeit( "dict(chain.from_iterable(g.items() for g in {k:d for k,d in data.items() if k != 'rock'}.values()))" , setup="from __main__ import data; from itertools import chain")
2.22612579818815
>>>

Upvotes: 1

Views: 36

Answers (2)

BENY
BENY

Reputation: 323226

Just need make your list of dict to dict (Fixing you code with adding the second line)

l=[v for v in {k:d for k,d in d.items() if k != 'rock'}.values()] # here is your own code 
newd=dict(kv for x in l for kv in x.items())
newd
Out[431]: 
{'gold1': {'data': ['g1']},
 'gold2': {'data': ['g2']},
 'gold3': {'data': ['g3']}}

With one-line

dict(v for d in d.values() for v in d.items()) # d is your dict
Out[436]: 
{'gold1': {'data': ['g1']},
 'gold2': {'data': ['g2']},
 'gold3': {'data': ['g3']}}

Upvotes: 1

blhsing
blhsing

Reputation: 106543

You can use a generator expression that outputs the items of the sub-dicts of the main dict's values, and use itertools.chain.from_iterable to join the items, and pass them to the dict constructor:

from itertools import chain
dict(chain.from_iterable(g.items() for g in d.values()))

so that given:

d = {'rock': {}, 'coal1': {'gold1': {'data': ['g1']}}, 'coal2': {'gold3': {'data': ['g3']}, 'gold2': {'data': ['g2']}}}

this returns:

{'gold3': {'data': ['g3']}, 'gold2': {'data': ['g2']}, 'gold1': {'data': ['g1']}}

Upvotes: 1

Related Questions