DEV Community

ohaddahan
ohaddahan

Posted on

3

Nested data structure extractors

Wanted to share a few helpful methods I use to traverse and extract data from nested data structures, usually 3rd party API responses.

input_data = {
  x: {
    z: {
      first_name: 'hello',
      last_name: 'world'
    }
  },
  q: [
    1,
    2,
    {
      z: {
        first_name: 'hello',
        last_name: 'bye'
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

First extraction method fetch_all_by_key , the purpose of this method is to extract all the elements with the same key (for example getting a list of similar objects in a response):



def fetch_all_by_key(element: nil, key: nil, container: [])
  if element.is_a?(Array)
    element.each { |i| fetch_all_by_key(element: i, key: key, container: container) }
  elsif element.is_a?(Hash)
    container << element.fetch(key) unless element.fetch(key, nil).nil?
    element.each_pair { |_, v| fetch_all_by_key(element: v, key: key, container: container) }
  end
  container
end

Enter fullscreen mode Exit fullscreen mode

Usage:

fetch_all_by_key(element: input_data, key: :q)
[
    [0] [
        [0] 1,
        [1] 2,
        [2] {
            :z => {
                :first_name => "hello",
                 :last_name => "bye"
            }
        }
    ]
]


fetch_all_by_key(element: input_data, key: :z)
[
    [0] {
        :first_name => "hello",
         :last_name => "world"
    },
    [1] {
        :first_name => "hello",
         :last_name => "bye"
    }
]
Enter fullscreen mode Exit fullscreen mode

The next method, is basically the same as the previous one, but will only fetch the first element which fits our requirement (when you care only about getting any element that fits your key):


def deep_fetch(element: nil, key: nil)
  ret_value = tmp = nil
  if element.is_a?(Array)
    element.each do |i|
      tmp = deep_fetch(element: i, key: key)
      break unless tmp.nil?
    end
  elsif element.is_a?(Hash)
    if element.has_key?(key)
      tmp = element[key]
    else
      element.each_pair do |_, v|
        tmp = deep_fetch(element: v, key: key)
        break unless tmp.nil?
      end
    end
  end
  ret_value = tmp unless tmp.nil?
  ret_value
end

deep_fetch(element: input_data, key: :q)
[
    [0] 1,
    [1] 2,
    [2] {
        :z => {
            :first_name => "hello",
             :last_name => "bye"
        }
    }
]

 deep_fetch(element: input_data, key: :z)
{
    :first_name => "hello",
     :last_name => "world"
}
2.6.3 :096 

Enter fullscreen mode Exit fullscreen mode

The last method uses the previous method to enforce order (sometimes the same key can appear in two different hierarchical paths hence you want to have more control over what you're fetching):


def multi_deep_fetch(element: nil, keys: [])
  while key = keys.shift
    element = deep_fetch(element: element, key: key)
  end
  element
end


multi_deep_fetch(element: input_data, keys: [:q])
[
    [0] 1,
    [1] 2,
    [2] {
        :z => {
            :first_name => "hello",
             :last_name => "bye"
        }
    }
]



 multi_deep_fetch(element: input_data, keys: [:y, :z])
nil

 multi_deep_fetch(element: input_data, keys: [:x, :z])
{
    :first_name => "hello",
     :last_name => "world"
}

multi_deep_fetch(element: input_data, keys: [:q, :z])
{
    :first_name => "hello",
     :last_name => "bye"
}

Enter fullscreen mode Exit fullscreen mode

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more