0

Is there something equivalent to Clojure's get-in function in Python? It gets the data at the given path in some data structure.

In Clojure it is used like:

(def employee
  {:name "John"
   :details {:email "info@domain.com"
             :phone "555-144-300"}})

(get-in employee [:details :email])  ; => "info@domain.com"

If translated to Python syntax it would be used like:

dictionary = {'a': {'b': 10}}
get_in(dictionary, ['a', 'b'])  # => 10

This function is used to access arbitrary data points in nestled data structures where the paths are not known at compile time, they are dynamic. More usage examples of get-in can be found on clojuredocs.

Rovanion
  • 4,382
  • 3
  • 29
  • 49

3 Answers3

2

No, but you can certainly make one

def get_in(d, keys):
   if not keys:
       return d
   return get_in(d[keys[0]], keys[1:])
FHTMitchell
  • 11,793
  • 2
  • 35
  • 47
  • 1
    [A bit more complete version](https://utgwkk.hateblo.jp/entry/2016/11/14/144209) outside Stack Overflow... (blog is Japanese, but code is code) – Amadan Aug 20 '18 at 11:23
  • @Amadan, Good one. – sat Aug 20 '18 at 11:25
  • This will throw an exception if a key is missing - which is different from the Clojure behaviour. – Aleph Aleph Aug 20 '18 at 11:28
  • 2
    @AlephAleph I saw your answer, didn't have to point it out here too... And in python we're a bit more happy to handle errors than in a functional language like closure. I'd rather get a `KeyError` than `None` which could be either a result or a fail. Let the user decide how they want to handle a fail. – FHTMitchell Aug 20 '18 at 11:30
1

You can write:

dictionary.get('details', {}).get('email')

This will safely get the value you need or None, without throwing an exception - just like Clojure's get-in does.

If you need a dedicated function for that, you can write:

def get_in(d, keys):
    if not keys:
        return d
    elif len(keys) == 1:
        return d.get(keys[0])
    else:
        return get_in(d.get(keys[0], {}), keys[1:])
Aleph Aleph
  • 5,215
  • 2
  • 13
  • 28
0

A simple for loop should solve your problem -

x = {'a':{'b':{'c': 1}}}

def get_element(elements, dictionary):
    for i in elements:
        try:
            dictionary = dictionary[i]
        except KeyError:
            return False
    return dictionary

print(get_element(['a', 'b', 'c'], x))
# 1
print(get_element(['a', 'z', 'c'], x))
# False
Sushant
  • 3,499
  • 3
  • 17
  • 34