DEV Community

Omar Bahareth
Omar Bahareth

Posted on

Are there functions similar to Ruby's `dig` in other languages?

I really like using Ruby's Array#dig and Hash#dig operator (introduced in Ruby 2.3) to quickly and safely access deeply nested structures

I would love to see different versions of it in other languages so I can use them more too!

Here's how dig works:

Assuming we have an orders array that looks like this.

orders = [
  {
    id: 1,
    customer: {
      name: "Customer 1",
      phone: "1234"
    }
  },

  {
    id: 2
  },

# ...
]
Enter fullscreen mode Exit fullscreen mode

We can easily navigate through this structure with dig like so

orders.dig(0, :customer, :phone) #=> "1234"
Enter fullscreen mode Exit fullscreen mode

We can also not worry about any of the "in-between" objects not existing, as it will return nil as soon it finds something that doesn't exist.

orders.dig(1, :customer, :phone) #=> nil
Enter fullscreen mode Exit fullscreen mode

It returns nil the moment it finds that customer doesn't exist, and it makes me avoid checking if keys exist every time I want to access a nested object.

What are some cool ways to access nested data like this in other languages? I ask because I want to learn and because I probably do it in overly-convoluted ways at the moment.

Thanks for reading!

Latest comments (15)

Collapse
 
gtors profile image
Andrey Torsunov

For Python, I created my own lib called diggity (available on PyPI):

from diggity import dig

data = {
    "users": [
        {
            "name": "Alice",
            "age": 30,
            "preferences": {
                "languages": ["Python", "Rust", "Go"]
            }
        },
    ]
}

dig(data, "users", 0, "name")  # "Alice"
dig(data, "users", 0, "hobby")  # None
dig(data, "users", 0, "hobby", default="coding")  # coding
Enter fullscreen mode Exit fullscreen mode
Collapse
 
devchris profile image
Christoph Drechsler

Now available for Javascript:

npmjs.com/package/jsdig

Collapse
 
patrickcarlohickman profile image
Patrick Carlo-Hickman

PHP7 introduced the null coalesce operator (??) which will suppress errors for the missing intermediate keys. So, this will work without errors/warnings:

$phone = $orders[0]['customer']['phone'] ?? null;

The Laravel framework in PHP also has array helpers which allow you to access values using "dot" notation:

$phone = array_get($orders, '0.customer.phone');

You can pass a third value to use as the default if it doesn't exist, but the default is null without it.

This helper function is a shortcut to access the Arr::get() method mentioned by Suhayb Alghutaymil.

Collapse
 
oshanwisumperuma profile image
Oshan Wisumperuma

In Elixir use Kernel.get_in/2 for maps

iex(1)> m = %{foo: %{bar: %{baz: 1}}}
%{foo: %{bar: %{baz: 1}}}
iex(2)> get_in m, [:foo, :bar, :baz]
1
iex(3)> get_in m, [:foo, :zot]
nil
Collapse
 
obahareth profile image
Omar Bahareth

Thank you for sharing everyone! I learned a lot!

Collapse
 
baweaver profile image
Brandon Weaver

Haskell and other functional languages have Lenses, which are roughly equivalent to focusing on a value or path and either getting or setting it. Imagine dig with a related bury function and you get close.

I'd mentioned it in this particular Storybook post in Part Four, though it may make more sense to start at Part One for continuity

Collapse
 
t3h2mas profile image
Thomas Noe

I'm a heavy user of Ramda's #path method in JavaScript / Node

Collapse
 
tiguchi profile image
Thomas Werner

In Java (version 8 and onward) this can be done using Optional

String phoneNumber = Optional.ofNullable(list.get(0))
                             .map(Order::getCustomer)
                             .map(Customer::getPhone)
                             .orElse(null);
Collapse
 
lazerfx profile image
Peter Street • Edited

C# 6.0 and onwards gives you the Null Propagating Operator MSDN Guide — like many of the other languages here, you would use it something like:

var customerName = orders[0]?.customer?.name;

This can be combined with the null coalescing operator, to give a default value:

var customerName = orders[0]?.customer?.name ?? "No value found.";
Collapse
 
ajkerrigan profile image
AJ Kerrigan • Edited

For Python, glom is a great package for this sort of nested data access. The happy path from the intro docs looks like this:

from glom import glom

target = {'a': {'b': {'c': 'd'}}}
glom(target, 'a.b.c')  # returns 'd'
Enter fullscreen mode Exit fullscreen mode

That 'a.b.c' bit is a simple glom specifier, but it can get much wilder! Glom provides more advanced specifiers for features like:

  • Null coalescing behavior
  • Validation
  • Debugging/inspection

And you can always plug in your own code or extend glom even further. Simple to get started, but a long fun road to travel from there!