Class: Hash
- Inherits:
-
Object
- Object
- Hash
- Defined in:
- lib/everythingrb/hash.rb
Overview
Extensions to Ruby’s core Hash class
Provides:
-
#to_struct, #to_ostruct, #to_istruct: Convert hashes to different structures
-
#join_map: Combine filter_map and join operations
-
#deep_freeze: Recursively freeze hash and contents
-
#transform_values.with_key: Transform values with access to keys
-
#value_where, #values_where: Find values based on conditions
-
#rename_key, #rename_keys: Rename hash keys while preserving order
-
::new_nested_hash: Create automatically nesting hashes
Constant Summary collapse
- EMPTY_STRUCT =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
A minimal empty struct for Ruby 3.2+ compatibility
Ruby 3.2 enforces stricter argument handling for Struct. This means trying to create a Struct from an empty Hash will result in an ArgumentError being raised. This is trying to keep a consistent experience with that version and newer versions.
Struct.new(:_).new(nil)
Class Method Summary collapse
-
.new_nested_hash(depth: nil) ⇒ Hash
Creates a new Hash that automatically initializes missing keys with nested hashes.
Instance Method Summary collapse
-
#deep_freeze ⇒ self
Recursively freezes self and all of its values.
-
#join_map(join_with = "") {|key, value| ... } ⇒ String
Combines filter_map and join operations.
-
#og_transform_values ⇒ Object
Allows calling original method.
-
#og_transform_values! ⇒ Object
Allows calling original method.
-
#rename_key(old_key, new_key) ⇒ Hash
Renames a key in the hash while preserving the original order of elements.
-
#rename_key!(old_key, new_key) ⇒ self
Renames a key in the hash in place while preserving the original order of elements.
-
#rename_key_unordered(old_key, new_key) ⇒ Hash
Renames a key in the hash without preserving element order (faster).
-
#rename_key_unordered!(old_key, new_key) ⇒ self
Renames a key in the hash in place without preserving element order (faster).
-
#rename_keys(**keys) ⇒ Hash
Renames multiple keys in the hash while preserving the original order of elements.
-
#rename_keys!(**keys) ⇒ self
Renames multiple keys in the hash in place while preserving the original order of elements.
-
#to_deep_h ⇒ Hash
Recursively converts all values that respond to #to_h.
-
#to_istruct ⇒ Data
Converts hash to an immutable Data structure.
-
#to_ostruct ⇒ OpenStruct
Converts hash to an OpenStruct recursively.
-
#to_struct ⇒ Struct
Converts hash to a Struct recursively.
-
#transform_values {|value| ... } ⇒ Hash, Enumerator
Transforms hash values while allowing access to keys via the chainable with_key method.
-
#transform_values! {|value| ... } ⇒ self, Enumerator
Transforms hash values in place while allowing access to keys via the chainable with_key method.
-
#value_where {|key, value| ... } ⇒ Object, ...
Returns the first value where the key-value pair satisfies the given condition.
-
#values_where {|key, value| ... } ⇒ Array, Enumerator
Returns all values where the key-value pairs satisfy the given condition.
Class Method Details
.new_nested_hash(depth: nil) ⇒ Hash
While unlimited nesting is convenient, it can interfere with common Ruby patterns like ||= when initializing values at deep depths. Use the depth parameter to control this behavior.
Creates a new Hash that automatically initializes missing keys with nested hashes
This method creates a hash where any missing key access will automatically create another nested hash with the same behavior. You can control the nesting depth with the depth parameter.
70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/everythingrb/hash.rb', line 70 def self.new_nested_hash(depth: nil) new do |hash, key| next if depth == 0 hash[key] = if depth.nil? new_nested_hash else new_nested_hash(depth: depth - 1) end end end |
Instance Method Details
#deep_freeze ⇒ self
CAUTION: Be careful when freezing collections that contain class objects or singleton instances - this will freeze those classes/objects globally! Only use deep_freeze on pure data structures you want to make immutable.
Recursively freezes self and all of its values
248 249 250 251 |
# File 'lib/everythingrb/hash.rb', line 248 def deep_freeze each_value { |v| v.respond_to?(:deep_freeze) ? v.deep_freeze : v.freeze } freeze end |
#join_map(join_with = "") {|key, value| ... } ⇒ String
Combines filter_map and join operations
102 103 104 105 106 |
# File 'lib/everythingrb/hash.rb', line 102 def join_map(join_with = "", &block) block = ->(kv_pair) { kv_pair.compact } if block.nil? filter_map(&block).join(join_with) end |
#og_transform_values ⇒ Object
Allows calling original method. See below
254 |
# File 'lib/everythingrb/hash.rb', line 254 alias_method :og_transform_values, :transform_values |
#og_transform_values! ⇒ Object
Allows calling original method. See below
257 |
# File 'lib/everythingrb/hash.rb', line 257 alias_method :og_transform_values!, :transform_values! |
#rename_key(old_key, new_key) ⇒ Hash
Renames a key in the hash while preserving the original order of elements
379 380 381 |
# File 'lib/everythingrb/hash.rb', line 379 def rename_key(old_key, new_key) rename_keys(old_key => new_key) end |
#rename_key!(old_key, new_key) ⇒ self
Renames a key in the hash in place while preserving the original order of elements
396 397 398 |
# File 'lib/everythingrb/hash.rb', line 396 def rename_key!(old_key, new_key) rename_keys!(old_key => new_key) end |
#rename_key_unordered(old_key, new_key) ⇒ Hash
Renames a key in the hash without preserving element order (faster)
This method is significantly faster than #rename_key but does not guarantee that the order of elements in the hash will be preserved.
458 459 460 461 462 463 464 465 |
# File 'lib/everythingrb/hash.rb', line 458 def rename_key_unordered(old_key, new_key) # Fun thing I learned. For small hashes, using #except is 1.5x faster than using dup and delete. # But as the hash becomes larger, the performance improvements become diminished until they're roughly the same. # Neat! hash = except(old_key) hash[new_key] = self[old_key] hash end |
#rename_key_unordered!(old_key, new_key) ⇒ self
Renames a key in the hash in place without preserving element order (faster)
This method is significantly faster than #rename_key! but does not guarantee that the order of elements in the hash will be preserved.
483 484 485 486 |
# File 'lib/everythingrb/hash.rb', line 483 def rename_key_unordered!(old_key, new_key) self[new_key] = delete(old_key) self end |
#rename_keys(**keys) ⇒ Hash
Renames multiple keys in the hash while preserving the original order of elements
This method maintains the original order of all keys in the hash, renaming only the specified keys while keeping their positions unchanged.
414 415 416 417 418 419 |
# File 'lib/everythingrb/hash.rb', line 414 def rename_keys(**keys) # I tried multiple different ways to rename the key while preserving the order, this was the fastest transform_keys do |key| keys.key?(key) ? keys[key] : key end end |
#rename_keys!(**keys) ⇒ self
Renames multiple keys in the hash in place while preserving the original order of elements
This method maintains the original order of all keys in the hash, renaming only the specified keys while keeping their positions unchanged.
436 437 438 439 440 441 |
# File 'lib/everythingrb/hash.rb', line 436 def rename_keys!(**keys) # I tried multiple different ways to rename the key while preserving the order, this was the fastest transform_keys! do |key| keys.key?(key) ? keys[key] : key end end |
#to_deep_h ⇒ Hash
Recursively converts all values that respond to #to_h
Similar to #to_h but recursively traverses the Hash structure and calls #to_h on any object that responds to it. Useful for normalizing nested data structures and parsing nested JSON.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/everythingrb/hash.rb', line 139 def to_deep_h transform_values do |value| case value when Hash value.to_deep_h when Array value.to_deep_h when String # If the string is not valid JSON, #to_deep_h will return `nil` value.to_deep_h || value else value.respond_to?(:to_h) ? value.to_h : value end end end |
#to_istruct ⇒ Data
Converts hash to an immutable Data structure
166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/everythingrb/hash.rb', line 166 def to_istruct recurse = lambda do |input| case input when Hash input.to_istruct when Array input.map(&recurse) else input end end Data.define(*keys.map(&:to_sym)).new(*values.map { |value| recurse.call(value) }) end |
#to_ostruct ⇒ OpenStruct
Converts hash to an OpenStruct recursively
220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/everythingrb/hash.rb', line 220 def to_ostruct recurse = lambda do |value| case value when Hash value.to_ostruct when Array value.map(&recurse) else value end end OpenStruct.new(**transform_values { |value| recurse.call(value) }) end |
#to_struct ⇒ Struct
Converts hash to a Struct recursively
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/everythingrb/hash.rb', line 192 def to_struct # For Ruby 3.2, it raises if you attempt to create a Struct with no keys return EMPTY_STRUCT if RUBY_VERSION.start_with?("3.2") && empty? recurse = lambda do |value| case value when Hash value.to_struct when Array value.map(&recurse) else value end end Struct.new(*keys.map(&:to_sym)).new(*values.map { |value| recurse.call(value) }) end |
#transform_values {|value| ... } ⇒ Hash, Enumerator
Transforms hash values while allowing access to keys via the chainable with_key method
This method either performs a standard transform_values operation if a block is given, or returns an enumerator with a with_key method that passes both the key and value to the block.
280 281 282 283 284 |
# File 'lib/everythingrb/hash.rb', line 280 def transform_values(&block) return transform_values_enumerator if block.nil? og_transform_values(&block) end |
#transform_values! {|value| ... } ⇒ self, Enumerator
Transforms hash values in place while allowing access to keys via the chainable with_key method
This method either performs a standard transform_values! operation if a block is given, or returns an enumerator with a with_key method that passes both the key and value to the block, updating the hash in place.
310 311 312 313 314 |
# File 'lib/everythingrb/hash.rb', line 310 def transform_values!(&block) return transform_values_bang_enumerator if block.nil? og_transform_values!(&block) end |
#value_where {|key, value| ... } ⇒ Object, ...
Returns the first value where the key-value pair satisfies the given condition
335 336 337 338 339 |
# File 'lib/everythingrb/hash.rb', line 335 def value_where(&block) return to_enum(:value_where) if block.nil? find(&block)&.last end |
#values_where {|key, value| ... } ⇒ Array, Enumerator
Returns all values where the key-value pairs satisfy the given condition
361 362 363 364 365 |
# File 'lib/everythingrb/hash.rb', line 361 def values_where(&block) return to_enum(:values_where) if block.nil? select(&block).values end |