Class: ElasticGraph::Support::HashUtil

Inherits:
Object
  • Object
show all
Defined in:
lib/elastic_graph/support/hash_util.rb

Class Method Summary collapse

Class Method Details

.deep_merge(hash1, hash2) ⇒ Object

Recursively merges the values from ‘hash2` into `hash1`, without mutating either `hash1` or `hash2`. When a key is in both `hash2` and `hash1`, takes the value from `hash2` just like `Hash#merge` does.

[View source]

67
68
69
70
71
72
73
74
75
76
# File 'lib/elastic_graph/support/hash_util.rb', line 67

def self.deep_merge(hash1, hash2)
  # `_ =` needed to satisfy steep--the types here are quite complicated.
  _ = hash1.merge(hash2) do |key, hash1_value, hash2_value|
    if ::Hash === hash1_value && ::Hash === hash2_value
      deep_merge(hash1_value, hash2_value)
    else
      hash2_value
    end
  end
end

.fetch_leaf_values_at_path(hash, key_path, &default) ⇒ Object

Fetches a list of (potentially) nested value from a hash. The ‘key_path` is expected to be a string with dots between the nesting levels (e.g. `foo.bar`). Returns `[]` if the value at any parent key is `nil`. Returns a flat array of values if the structure at any level is an array.

Raises an error if the key is not found unless a default block is provided. Raises an error if any parent value is not a hash as expected. Raises an error if the provided path is not a full path to a leaf in the nested structure.

[View source]

86
87
88
# File 'lib/elastic_graph/support/hash_util.rb', line 86

def self.fetch_leaf_values_at_path(hash, key_path, &default)
  do_fetch_leaf_values_at_path(hash, key_path.split("."), 0, &default)
end

.fetch_value_at_path(hash, key_path) ⇒ Object

Fetches a single value from the hash at the given path. The ‘key_path` is expected to be a string with dots between the nesting levels (e.g. `foo.bar`).

If any parent value is not a hash as expected, raises an error. If the key at any level is not found, yields to the provided block (which can provide a default value) or raises an error if no block is provided.

[View source]

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/elastic_graph/support/hash_util.rb', line 96

def self.fetch_value_at_path(hash, key_path)
  path_parts = key_path.split(".")

  path_parts.each.with_index(1).reduce(hash) do |inner_hash, (key, num_parts)|
    if inner_hash.is_a?(::Hash)
      inner_hash.fetch(key) do
        missing_path = path_parts.first(num_parts).join(".")
        return yield missing_path if block_given?
        raise KeyError, "Key not found: #{missing_path.inspect}"
      end
    else
      raise KeyError, "Value at key #{path_parts.first(num_parts - 1).join(".").inspect} is not a `Hash` as expected; " \
        "instead, was a `#{(_ = inner_hash).class}`"
    end
  end
end

.flatten_and_stringify_keys(source_hash, prefix: nil) ⇒ Object

Recursively flattens the provided source hash, converting keys to strings along the way with dots used to separate nested parts. For example:

flatten_and_stringify_keys({ a: { b: 3 }, c: 5 }, prefix: “foo”) returns: { “foo.a.b” => 3, “foo.c” => 5 }

[View source]

56
57
58
59
60
61
62
63
# File 'lib/elastic_graph/support/hash_util.rb', line 56

def self.flatten_and_stringify_keys(source_hash, prefix: nil)
  # @type var flat_hash: ::Hash[::String, untyped]
  flat_hash = {}
  prefix = prefix ? "#{prefix}." : ""
  # `_ =` is needed by steep because it thinks `prefix` could be `nil` in spite of the above line.
  populate_flat_hash(source_hash, _ = prefix, flat_hash)
  flat_hash
end

.recursively_prune_nils_and_empties_from(object, &block) ⇒ Object

Recursively prunes nil values or empty hash/array values from the hash, at any level of its structure, without mutating the provided argument. Key paths that are pruned are yielded to the caller to allow the caller to have awareness of what was pruned.

[View source]

41
42
43
44
45
46
47
48
49
# File 'lib/elastic_graph/support/hash_util.rb', line 41

def self.recursively_prune_nils_and_empties_from(object, &block)
  recursively_prune_if(object, block) do |value|
    if value.is_a?(::Hash) || value.is_a?(::Array)
      value.empty?
    else
      value.nil?
    end
  end
end

.recursively_prune_nils_from(object, &block) ⇒ Object

Recursively prunes nil values from the hash, at any level of its structure, without mutating the provided argument. Key paths that are pruned are yielded to the caller to allow the caller to have awareness of what was pruned.

[View source]

34
35
36
# File 'lib/elastic_graph/support/hash_util.rb', line 34

def self.recursively_prune_nils_from(object, &block)
  recursively_prune_if(object, block, &:nil?)
end

.stringify_keys(object) ⇒ Object

Recursively transforms any hash keys in the given object to string keys, without mutating the provided argument.

[View source]

14
15
16
17
18
# File 'lib/elastic_graph/support/hash_util.rb', line 14

def self.stringify_keys(object)
  recursively_transform(object) do |key, value, hash|
    hash[key.to_s] = value
  end
end

.symbolize_keys(object) ⇒ Object

Recursively transforms any hash keys in the given object to symbol keys, without mutating the provided argument.

Important note: this should never be used on untrusted input. Symbols are not GCd in Ruby in the same way as strings.

[View source]

25
26
27
28
29
# File 'lib/elastic_graph/support/hash_util.rb', line 25

def self.symbolize_keys(object)
  recursively_transform(object) do |key, value, hash|
    hash[key.to_sym] = value
  end
end