Class: Hash
- Inherits:
-
Object
- Object
- Hash
- Includes:
- Everythingrb::InspectQuotable
- Defined in:
- lib/everythingrb/hash.rb
Overview
Extensions to Ruby's core Hash class
Provides:
- #to_struct, #to_ostruct, #to_datum: Convert hashes to different structures (#to_istruct is deprecated, use #to_datum)
- #join_map: Combine filter_map and join operations
- #transform_values.with_key: Transform values with access to keys
- #transform, #transform!: Transform keys and values
- #find_value, #select_values: Find values based on conditions
- #rename_key, #rename_keys: Rename hash keys while preserving order
- #merge_if, #merge_if!: Conditionally merge based on key-value pairs
- #merge_if_values, #merge_if_values!: Conditionally merge based on values
- #compact_merge, #compact_merge!: Merge only non-nil values
- #compact_blank_merge, #compact_blank_merge!: Merge only present values (ActiveSupport)
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)
Instance Method Summary collapse
-
#compact_blank_merge(other = {}) ⇒ Hash
Merges only present (non-blank) values from another hash.
-
#compact_blank_merge!(other = {}) ⇒ self
Merges only present (non-blank) values from another hash, in place.
-
#compact_merge(other = {}) ⇒ Hash
Merges only non-nil values from another hash.
-
#compact_merge!(other = {}) ⇒ self
Merges only non-nil values from another hash, in place.
-
#deep_transform_values(with_key: false) {|value, key| ... } ⇒ Hash, Enumerator
Recursively transforms all values in the hash and nested structures.
-
#deep_transform_values!(with_key: false) {|value, key| ... } ⇒ self, Enumerator
Recursively transforms all values in the hash and nested structures in place.
-
#find_value {|key, value| ... } ⇒ Object, ...
Returns the first value where the key-value pair satisfies the given condition.
-
#join_map(join_with = "", with_index: false) {|key_value_pair, index| ... } ⇒ String
Combines filter_map and join operations.
-
#merge_if(other = {}) {|key, value| ... } ⇒ Hash
Conditionally merges key-value pairs from another hash based on a block.
-
#merge_if!(other = {}) {|key, value| ... } ⇒ self
Conditionally merges key-value pairs from another hash in place.
-
#merge_if_values(other = {}) {|value| ... } ⇒ Hash
Conditionally merges key-value pairs based only on values.
-
#merge_if_values!(other = {}) {|value| ... } ⇒ self
Conditionally merges key-value pairs based only on values, in place.
-
#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.
-
#select_values {|key, value| ... } ⇒ Array, Enumerator
Returns all values where the key-value pairs satisfy the given condition.
-
#to_datum ⇒ Datum
Recursively converts hash to an immutable Datum.
-
#to_istruct ⇒ Datum
deprecated
Deprecated.
Use #to_datum instead. Will be removed in v2.0.0.
-
#to_ostruct ⇒ OpenStruct
Converts hash to an OpenStruct recursively.
-
#to_struct ⇒ Struct
Converts hash to a Struct recursively.
-
#transform {|key, value| ... } ⇒ Hash, Enumerator
Transforms keys and values to create a new hash.
-
#transform! {|key, value| ... } ⇒ self, Enumerator
Transforms keys and values in place.
-
#transform_values(with_key: false) {|value, key| ... } ⇒ Hash, Enumerator
Returns a new hash with all values transformed by the block.
-
#transform_values!(with_key: false) {|value, key| ... } ⇒ self, Enumerator
Transforms all values in the hash in place.
Methods included from Everythingrb::InspectQuotable
Instance Method Details
#compact_blank_merge(other = {}) ⇒ Hash
Only available when ActiveSupport is loaded
Merges only present (non-blank) values from another hash
This method merges key-value pairs from another hash, but only includes values that are present according to ActiveSupport's definition (not nil, not empty strings, not empty arrays, etc.).
768 769 770 |
# File 'lib/everythingrb/hash.rb', line 768 def compact_blank_merge(other = {}) merge_if_values(other) { |i| i.present? } end |
#compact_blank_merge!(other = {}) ⇒ self
Only available when ActiveSupport is loaded
Merges only present (non-blank) values from another hash, in place
This method merges key-value pairs from another hash into the current hash, but only includes values that are present according to ActiveSupport's definition (not nil, not empty strings, not empty arrays, etc.).
794 795 796 |
# File 'lib/everythingrb/hash.rb', line 794 def compact_blank_merge!(other = {}) merge_if_values!(other) { |i| i.present? } end |
#compact_merge(other = {}) ⇒ Hash
Merges only non-nil values from another hash
This is a convenience method for the common pattern of merging only values that are not nil.
710 711 712 |
# File 'lib/everythingrb/hash.rb', line 710 def compact_merge(other = {}) merge_if_values(other) { |i| i.itself } end |
#compact_merge!(other = {}) ⇒ self
Merges only non-nil values from another hash, in place
This is a convenience method for the common pattern of merging only values that are not nil.
733 734 735 |
# File 'lib/everythingrb/hash.rb', line 733 def compact_merge!(other = {}) merge_if_values!(other) { |i| i.itself } end |
#deep_transform_values(with_key: false) {|value, key| ... } ⇒ Hash, Enumerator
Recursively transforms all values in the hash and nested structures
Walks through the hash and all nested hashes/arrays and yields each non-hash and non-array value to the block, replacing it with the block's return value.
294 295 296 297 298 299 300 301 302 |
# File 'lib/everythingrb/hash.rb', line 294 def deep_transform_values(with_key: false, &block) return to_enum(:deep_transform_values, with_key:) if block.nil? if with_key _deep_transform_values_with_key(self, nil, &block) else og_deep_transform_values(&block) end end |
#deep_transform_values!(with_key: false) {|value, key| ... } ⇒ self, Enumerator
Recursively transforms all values in the hash and nested structures in place
Same as #deep_transform_values but modifies the hash in place.
331 332 333 334 335 336 337 338 339 |
# File 'lib/everythingrb/hash.rb', line 331 def deep_transform_values!(with_key: false, &block) return to_enum(:deep_transform_values!, with_key:) if block.nil? if with_key _deep_transform_values_with_key!(self, nil, &block) else og_deep_transform_values!(&block) end end |
#find_value {|key, value| ... } ⇒ Object, ...
Returns the first value where the key-value pair satisfies the given condition
440 441 442 443 444 |
# File 'lib/everythingrb/hash.rb', line 440 def find_value(&block) return to_enum(:find_value) if block.nil? find(&block)&.last end |
#join_map(join_with = "", with_index: false) {|key_value_pair, index| ... } ⇒ String
Combines filter_map and join operations
66 67 68 69 70 71 72 73 74 |
# File 'lib/everythingrb/hash.rb', line 66 def join_map(join_with = "", with_index: false, &block) block = ->(kv_pair) { kv_pair.compact } if block.nil? if with_index filter_map.with_index(&block).join(join_with) else filter_map(&block).join(join_with) end end |
#merge_if(other = {}) {|key, value| ... } ⇒ Hash
Conditionally merges key-value pairs from another hash based on a block
620 621 622 623 624 |
# File 'lib/everythingrb/hash.rb', line 620 def merge_if(other = {}, &block) other = other.select(&block) unless block.nil? merge(other) end |
#merge_if!(other = {}) {|key, value| ... } ⇒ self
Conditionally merges key-value pairs from another hash in place
643 644 645 646 647 |
# File 'lib/everythingrb/hash.rb', line 643 def merge_if!(other = {}, &block) other = other.select(&block) unless block.nil? merge!(other) end |
#merge_if_values(other = {}) {|value| ... } ⇒ Hash
Conditionally merges key-value pairs based only on values
664 665 666 |
# File 'lib/everythingrb/hash.rb', line 664 def merge_if_values(other = {}, &block) merge_if(other) { |k, v| block.call(v) } end |
#merge_if_values!(other = {}) {|value| ... } ⇒ self
Conditionally merges key-value pairs based only on values, in place
684 685 686 |
# File 'lib/everythingrb/hash.rb', line 684 def merge_if_values!(other = {}, &block) merge_if!(other) { |k, v| block.call(v) } end |
#rename_key(old_key, new_key) ⇒ Hash
Renames a key in the hash while preserving the original order of elements
484 485 486 |
# File 'lib/everythingrb/hash.rb', line 484 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
501 502 503 |
# File 'lib/everythingrb/hash.rb', line 501 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.
563 564 565 566 567 568 569 570 571 572 573 574 |
# File 'lib/everythingrb/hash.rb', line 563 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) # Only modify the hash if the old key exists return hash unless key?(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.
592 593 594 595 596 597 598 |
# File 'lib/everythingrb/hash.rb', line 592 def rename_key_unordered!(old_key, new_key) # Only modify the hash if the old key exists return self unless key?(old_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.
519 520 521 522 523 524 |
# File 'lib/everythingrb/hash.rb', line 519 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.
541 542 543 544 545 546 |
# File 'lib/everythingrb/hash.rb', line 541 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 |
#select_values {|key, value| ... } ⇒ Array, Enumerator
Returns all values where the key-value pairs satisfy the given condition
466 467 468 469 470 |
# File 'lib/everythingrb/hash.rb', line 466 def select_values(&block) return to_enum(:select_values) if block.nil? select(&block).values end |
#to_datum ⇒ Datum
Recursively converts hash to an immutable Datum
Nested hashes and arrays are converted too. Unlike a raw Data object, two Datums with the same attributes are equal, so the result is safe to compare or use as a Hash key.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/everythingrb/hash.rb', line 90 def to_datum recurse = lambda do |value| case value when Hash value.to_datum when Array value.map(&recurse) else value end end Datum.new(transform_values { |value| recurse.call(value) }) end |
#to_istruct ⇒ Datum
Use #to_datum instead. Will be removed in v2.0.0.
Recursively converts hash to an immutable Data structure
117 118 119 120 121 122 123 124 |
# File 'lib/everythingrb/hash.rb', line 117 def to_istruct Everythingrb.deprecator.warn( "Hash#to_istruct is deprecated and will be removed in v2.0.0. " \ "Use Hash#to_datum instead." ) to_datum end |
#to_ostruct ⇒ OpenStruct
Converts hash to an OpenStruct recursively
165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/everythingrb/hash.rb', line 165 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
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/everythingrb/hash.rb', line 137 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 {|key, value| ... } ⇒ Hash, Enumerator
Transforms keys and values to create a new hash
393 394 395 396 397 |
# File 'lib/everythingrb/hash.rb', line 393 def transform(&block) return to_enum(:transform) if block.nil? to_h(&block) end |
#transform! {|key, value| ... } ⇒ self, Enumerator
Transforms keys and values in place
415 416 417 418 419 |
# File 'lib/everythingrb/hash.rb', line 415 def transform!(&block) return to_enum(:transform!) if block.nil? replace(transform(&block)) end |
#transform_values(with_key: false) {|value, key| ... } ⇒ Hash, Enumerator
Returns a new hash with all values transformed by the block
Enhances Ruby's standard transform_values with key access capability.
209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/everythingrb/hash.rb', line 209 def transform_values(with_key: false, &block) return to_enum(:transform_values, with_key:) if block.nil? if with_key each_pair.with_object({}) do |(key, value), output| output[key] = block.call(value, key) end else og_transform_values(&block) end end |
#transform_values!(with_key: false) {|value, key| ... } ⇒ self, Enumerator
Transforms all values in the hash in place
Enhances Ruby's standard transform_values! with key access capability.
248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/everythingrb/hash.rb', line 248 def transform_values!(with_key: false, &block) return to_enum(:transform_values!, with_key:) if block.nil? if with_key each_pair do |key, value| self[key] = block.call(value, key) end else og_transform_values!(&block) end end |