4

I have some dictionaries(json output). I want to get the base element which can be a list of strings or a string. Currently I am doing it like this:-

folder="shared/"
files=os.listdir('shared')


for f in files:
    f=folder+f
    print(f)
    with open(f) as f:
        data = json.load(f)
    #data is a dict now with sub-keys
    for key,value in data.items():
        if value.keys():
            print(value)
    break

This is the input dictionary that was read by the python code:-

{
  "shortshirt": {
    "ralphlauren": {
      "classic": [
        "That Ralph Lauren classic fit is a timeless look!",
        "Nice choice. Can’t go wrong with Ralph Lauren"
      ]
    }
  },
  "socks": {
    "": {
      "": ["Have to find the right socks to keep your feet cozy"]
    }
  }
}

And this is the output that I am getting:-

{'ralphlauren': {'classic': ['That Ralph Lauren classic fit is a timeless look!', 'Nice choice. Can’t go wrong with Ralph Lauren']}}
{'': {'': ['Have to find the right socks to keep your feet cozy']}}

But this is what I want:-

keys=[["shortshirt","ralphlauren","classic"],["socks"]]

value=[['That Ralph Lauren classic fit is a timeless look!', 'Nice choice. Can’t go wrong with Ralph Lauren'], ['Have to find the right socks to keep your feet cozy']]

But I don't know whether to have 2 or 3 level nested loops. If I have an inner loop and in actual there was no nested key then I get the value error. I want to get all the nested keys in a separate list and the base value or values like on the lowest level in another list, any help regarding this will be highly appreciated.

meowgoesthedog
  • 14,670
  • 4
  • 27
  • 40
Asim
  • 1,430
  • 1
  • 22
  • 43
  • 2
    Your description is unclear. Provide the corresponding **expected** output for your sample data. – meowgoesthedog Mar 19 '19 at 11:23
  • I have edited the question. – Asim Mar 19 '19 at 11:27
  • Well, this is because you are not going down the dictionary, so you are only getting the top-level values (that are dicts). For what you want you have to go down the dictionary until the value is not a dict. You could do it recursively. Same thing for the keys, and don't forget to filter the empty ones. – Javier Paz Sedano Mar 19 '19 at 11:49
  • if I go down (add a loop) then how do I know how much deep should I go? Like I mean, I am going to write the code first, if I write 3 loops and the actual has 5 sub-keys, how will I get those? – Asim Mar 19 '19 at 12:06
  • recursion, as @JavierPazSedano mentioned, then it would not matter how many sub-keys exist – gold_cy Mar 19 '19 at 12:41

1 Answers1

4

Generators are useful for this problem. Strategies –

  • Keys: keep track of the current recursion path. Yield the current path as soon as you hit a leaf.

  • Values: only yield leaves.

Code:

def getitems(obj):

  def getkeys(obj, stack):
    for k, v in obj.items():
      k2 = ([k] if k else []) + stack # don't return empty keys
      if v and isinstance(v, dict):
        for c in getkeys(v, k2):
          yield c
      else: # leaf
        yield k2

  def getvalues(obj):
    for v in obj.values():
      if not v: continue
      if isinstance(v, dict):
        for c in getvalues(v):
          yield c
      else: # leaf
        yield v if isinstance(v, list) else [v]

  return list(getkeys(obj,[])), list(getvalues(obj))

Input:

{
  "shortshirt": {
    "ralphlauren": {
      "classic": [
        "That Ralph Lauren classic fit is a timeless look!",
        "Nice choice. Can't go wrong with Ralph Lauren"
      ]
    }
  },
  "socks": {
    "": {
      "": ["Have to find the right socks to keep your feet cozy"]
    }
  }
}

Output:

# keys
[['classic', 'ralphlauren', 'shortshirt'], ['socks']]

# values
[['That Ralph Lauren classic fit is a timeless look!', "Nice choice. Can't go wrong with Ralph Lauren"], ['Have to find the right socks to keep your feet cozy']]
meowgoesthedog
  • 14,670
  • 4
  • 27
  • 40