-1

I have been trying to figure out how to KISS when it comes to get value from a dict.

There is various scenarios such as:

  1. The key does not exists (Should return default {}/[]/"")
  2. The key contains a value of empty list (Should then return empty list)
  3. If we trying to grab a value e.g. test['objects'][0]['productInfo'][0]['merchPrice']['promoExclusions'][0] and if it already fails at productInfo, it should imminently return the default instead of continuing searching for the next key

I have ended up doing something like this:

test = {
  'pages': {
    'prev': '',
  },
  'objects': [
    {
      'id': 'c362b8f3-1862-4e2d-ba06-d910e0d98e7e',
      'productInfo': [
        {
          'merchProduct': {
            'id': '63912b18-f00f-543f-a5c5-0c6236f63e79',
            'snapshotId': '43cf801e-3689-42c2-ac85-d404e69aba42',

          },
          'merchPrice': {
            'id': '7dd81061-d933-57f6-b233-2a6418ce487d',
            'snapshotId': '268cc5af-8e04-4d64-b19b-02c2770b91fb',
            'discounted': False,
            #Could be | 'promoExclusions': ['TRUE'],
            'promoExclusions': [],
            'resourceType': 'merchPrice',
          }
        }
      ]
    }
  ]
}


if test_print := test['objects'][0]['productInfo'][0]['merchPrice']['promoExclusions'][0]:
   print(test_print)

This however returns an error IndexError: list index out of range due to the list does not contain any value which make sense but I wonder how I could in that case set a default value for this particle problem.

My question is: How can I keep it as simple as possible and efficiently to find and get the value of a dict without needing to use alot of resources and if its not found then return a default value?

Barmar
  • 741,623
  • 53
  • 500
  • 612
PythonNewbie
  • 1,031
  • 1
  • 15
  • 33
  • It looks like you have JSON there, have you tried using a JSON parser to handle all of that for you? – RoadieRich Sep 20 '21 at 16:54
  • HI @RoadieRich Hmmm.. It sure is JSON but also a dict, if you do `print(type(test))` that would return `` so I believe its a dict as well. However I am not sure how the JSON parser would handle it for me? – PythonNewbie Sep 20 '21 at 16:55
  • 1
    @RoadieRich Where do you see JSON? Are you talking about the dict? – wjandrea Sep 20 '21 at 17:00
  • A try / catch would be keeping it simple imo – ScootCork Sep 20 '21 at 17:04
  • 1
    The normal solution is to catch `KeyError` and `IndexError` (dict and list) then assign a default. You won't know where in the reference chain th problem was, but I'm not sure that matters in your case. – tdelaney Sep 20 '21 at 17:04
  • @RoadieRich - JavaScript Object Notation (JSON) is a serialization format. It looks similar to python literals like what we have here, but its not the same thing. The python parser creaed the dict and list objects here. No need for json. – tdelaney Sep 20 '21 at 17:07
  • 1
    Related: [How to get the nth element of a python list or a default if not available](https://stackoverflow.com/q/2492087/4518341), [Safe method to get value of nested dictionary](https://stackoverflow.com/q/25833613/4518341), [Access nested dictionary items via a list of keys?](https://stackoverflow.com/q/14692690/4518341) – wjandrea Sep 20 '21 at 17:12
  • @wjandrea Thanks for the link! – PythonNewbie Sep 20 '21 at 17:20

3 Answers3

4

For a single dict you could use d.get('key', default), but there's no simple shortcut for a string of nested extractions. You can do it with try/except

try:
    test_print = test['objects'][0]['productInfo'][0]['merchPrice']['promoExclusions'][0]
except KeyError:
    test_print = default_value
print(test_print)
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Right, That make sense :) What I was thinking, or the reason why I didn't do the try except principle was due to that if I wanted to get multiple keys. Say around 20 different keys, the code would be pretty long where each `get key` would need to be covered in try/except. My thought was that if I could do something like `if the key is in the json then get the value` perhaps? EDIT: I am not saying your way is wrong at all, just a taught I had that I wanted to make it even easier if there was a way of course as I wanted to keep the code as little as possible :) – PythonNewbie Sep 20 '21 at 17:09
  • You can use https://stackoverflow.com/questions/14692690/access-nested-dictionary-items-via-a-list-of-keys to access nested data from a list of keys, and the loop can return the default if it can't find the key. – Barmar Sep 20 '21 at 17:10
  • Then we are starting to look at more "heavier" cpu usage to go through eaceh loop everytime I want to get a key in a nested dict, wouldn't it? – PythonNewbie Sep 20 '21 at 17:11
  • As I said, there's no shortcut for testing. You can use `if key in dict`, but not `if nested_keys in nested_dicts_and_arrays` – Barmar Sep 20 '21 at 17:11
  • The loop overhead is negligible. – Barmar Sep 20 '21 at 17:12
  • Thats also what I thought. I was expecting some people to mention the `.get(key, {})` but nobody didm Surprisly! – PythonNewbie Sep 20 '21 at 17:21
2

Have you tried using try except ? The snippet below prints the value of the dictionary that you are trying to access, or prints default in the event of an exception.

default = 'default {}/[]/""'
try:
    print(test['objects'][0]['productInfo'][0]['merchPrice']['promoExclusions'][0])
except (KeyError, IndexError):
    print(default)
Ricardo
  • 118
  • 8
0

How can I keep it as simple as possible and efficiently to find and get the value of a dict without needing to use alot of resources and if its not found then return a default value?

The answer to this question is to use collections.defaultdict:

>>> import collections
>>> d = collections.defaultdict(list, {'key1': [1], 'key2': [2]})
>>> d['key1']
[1]
>>> d['key3']
[]

RoadieRich
  • 6,330
  • 3
  • 35
  • 52