Class: ElasticGraph::Support::HashUtil
- Inherits:
-
Object
- Object
- ElasticGraph::Support::HashUtil
- Defined in:
- lib/elastic_graph/support/hash_util.rb
Class Method Summary collapse
-
.deep_merge(hash1, hash2) ⇒ Object
Recursively merges the values from ‘hash2` into `hash1`, without mutating either `hash1` or `hash2`.
-
.disjoint_merge(hash1, hash2) ⇒ Object
Like ‘Hash#merge`, but verifies that the hashes were strictly disjoint (e.g. had no keys in common).
-
.fetch_leaf_values_at_path(hash, key_path, &default) ⇒ Object
Fetches a list of (potentially) nested value from a hash.
-
.fetch_value_at_path(hash, key_path) ⇒ Object
Fetches a single value from the hash at the given path.
-
.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.
-
.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.
-
.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.
-
.strict_to_h(pairs) ⇒ Object
Like ‘Hash#to_h`, but strict.
-
.stringify_keys(object) ⇒ Object
Recursively transforms any hash keys in the given object to string keys, without mutating the provided argument.
-
.symbolize_keys(object) ⇒ Object
Recursively transforms any hash keys in the given object to symbol keys, without mutating the provided argument.
-
.verbose_fetch(hash, key) ⇒ Object
Fetches a key from a hash (just like ‘Hash#fetch`) but with a more verbose error message when the key is not found.
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.
104 105 106 107 108 109 110 111 112 113 |
# File 'lib/elastic_graph/support/hash_util.rb', line 104 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 |
.disjoint_merge(hash1, hash2) ⇒ Object
Like ‘Hash#merge`, but verifies that the hashes were strictly disjoint (e.g. had no keys in common). An error is raised if they do have any keys in common.
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/elastic_graph/support/hash_util.rb', line 35 def self.disjoint_merge(hash1, hash2) conflicting_keys = [] # : ::Array[untyped] merged = hash1.merge(hash2) do |key, v1, _v2| conflicting_keys << key v1 end unless conflicting_keys.empty? raise ::KeyError, "Hashes were not disjoint. Conflicting keys: #{conflicting_keys.inspect}." end merged 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.
123 124 125 |
# File 'lib/elastic_graph/support/hash_util.rb', line 123 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.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/elastic_graph/support/hash_util.rb', line 133 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 }
93 94 95 96 97 98 99 100 |
# File 'lib/elastic_graph/support/hash_util.rb', line 93 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.
78 79 80 81 82 83 84 85 86 |
# File 'lib/elastic_graph/support/hash_util.rb', line 78 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.
71 72 73 |
# File 'lib/elastic_graph/support/hash_util.rb', line 71 def self.recursively_prune_nils_from(object, &block) recursively_prune_if(object, block, &:nil?) end |
.strict_to_h(pairs) ⇒ Object
Like ‘Hash#to_h`, but strict. When the given input has conflicting keys, `Hash#to_h` will happily let the last pair when. This method instead raises an exception.
22 23 24 25 26 27 28 29 30 31 |
# File 'lib/elastic_graph/support/hash_util.rb', line 22 def self.strict_to_h(pairs) hash = pairs.to_h if hash.size < pairs.size conflicting_keys = pairs.map(&:first).tally.filter_map { |key, count| key if count > 1 } raise ::KeyError, "Cannot build a strict hash, since input has conflicting keys: #{conflicting_keys.inspect}." end hash end |
.stringify_keys(object) ⇒ Object
Recursively transforms any hash keys in the given object to string keys, without mutating the provided argument.
51 52 53 54 55 |
# File 'lib/elastic_graph/support/hash_util.rb', line 51 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.
62 63 64 65 66 |
# File 'lib/elastic_graph/support/hash_util.rb', line 62 def self.symbolize_keys(object) recursively_transform(object) do |key, value, hash| hash[key.to_sym] = value end end |
.verbose_fetch(hash, key) ⇒ Object
Fetches a key from a hash (just like ‘Hash#fetch`) but with a more verbose error message when the key is not found. The error message indicates the available keys unlike `Hash#fetch`.
14 15 16 17 18 |
# File 'lib/elastic_graph/support/hash_util.rb', line 14 def self.verbose_fetch(hash, key) hash.fetch(key) do raise ::KeyError, "key not found: #{key.inspect}. Available keys: #{hash.keys.inspect}." end end |