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_istruct: Convert hashes to different structures
-
#join_map: Combine filter_map and join operations
-
#transform_values.with_key: Transform values with access to keys
-
#transform, #transform!: Transform keys and values
-
#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
-
#merge_if, #merge_if!: Conditionally merge based on key-value pairs
-
#merge_if_values, #merge_if_values!: Conditionally merge based on values
-
#merge_compact, #merge_compact!: Merge only non-nil values
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_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.
-
#join_map(join_with = "") {|key, value| ... } ⇒ String
Combines filter_map and join operations.
-
#merge_compact(other = {}) ⇒ Hash
Merges only non-nil values from another hash.
-
#merge_compact!(other = {}) ⇒ self
Merges only non-nil values from another hash, in place.
-
#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.
-
#reject_values {|value| ... } ⇒ Hash, Enumerator
Rejects hash entries based only on their values.
-
#reject_values! {|value| ... } ⇒ self, ...
Rejects hash entries based only on their values, modifying the hash 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 {|value| ... } ⇒ Hash, Enumerator
(also: #filter_values)
Selects hash entries based only on their values.
-
#select_values! {|value| ... } ⇒ self, ...
(also: #filter_values!)
Selects hash entries based only on their values, modifying the hash in place.
-
#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 {|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.
-
#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.
Methods included from Everythingrb::InspectQuotable
Class Method Details
.new_nested_hash(depth: nil) ⇒ Hash
This implementation is not thread-safe for concurrent modifications of deeply nested structures. If you need thread safety, consider using a mutex when modifying the deeper levels of the 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.
79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/everythingrb/hash.rb', line 79 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_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.
358 359 360 361 362 363 364 365 366 |
# File 'lib/everythingrb/hash.rb', line 358 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.
395 396 397 398 399 400 401 402 403 |
# File 'lib/everythingrb/hash.rb', line 395 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 |
#join_map(join_with = "") {|key, value| ... } ⇒ String
Combines filter_map and join operations
111 112 113 114 115 |
# File 'lib/everythingrb/hash.rb', line 111 def join_map(join_with = "", &block) block = ->(kv_pair) { kv_pair.compact } if block.nil? filter_map(&block).join(join_with) end |
#merge_compact(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.
863 864 865 |
# File 'lib/everythingrb/hash.rb', line 863 def merge_compact(other = {}) merge_if_values(other, &:itself) end |
#merge_compact!(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.
886 887 888 |
# File 'lib/everythingrb/hash.rb', line 886 def merge_compact!(other = {}) merge_if_values!(other, &:itself) end |
#merge_if(other = {}) {|key, value| ... } ⇒ Hash
Conditionally merges key-value pairs from another hash based on a block
773 774 775 776 777 |
# File 'lib/everythingrb/hash.rb', line 773 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
796 797 798 799 800 |
# File 'lib/everythingrb/hash.rb', line 796 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
817 818 819 |
# File 'lib/everythingrb/hash.rb', line 817 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
837 838 839 |
# File 'lib/everythingrb/hash.rb', line 837 def merge_if_values!(other = {}, &block) merge_if!(other) { |k, v| block.call(v) } end |
#reject_values {|value| ... } ⇒ Hash, Enumerator
Rejects hash entries based only on their values
725 726 727 728 729 |
# File 'lib/everythingrb/hash.rb', line 725 def reject_values(&block) return to_enum(:reject_values) if block.nil? reject { |_k, v| block.call(v) } end |
#reject_values! {|value| ... } ⇒ self, ...
Rejects hash entries based only on their values, modifying the hash in place
747 748 749 750 751 |
# File 'lib/everythingrb/hash.rb', line 747 def reject_values!(&block) return to_enum(:reject_values!) if block.nil? reject! { |_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
548 549 550 |
# File 'lib/everythingrb/hash.rb', line 548 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
565 566 567 |
# File 'lib/everythingrb/hash.rb', line 565 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.
627 628 629 630 631 632 633 634 |
# File 'lib/everythingrb/hash.rb', line 627 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.
652 653 654 655 |
# File 'lib/everythingrb/hash.rb', line 652 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.
583 584 585 586 587 588 |
# File 'lib/everythingrb/hash.rb', line 583 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.
605 606 607 608 609 610 |
# File 'lib/everythingrb/hash.rb', line 605 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 {|value| ... } ⇒ Hash, Enumerator Also known as: filter_values
Selects hash entries based only on their values
675 676 677 678 679 |
# File 'lib/everythingrb/hash.rb', line 675 def select_values(&block) return to_enum(:select_values) if block.nil? select { |_k, v| block.call(v) } end |
#select_values! {|value| ... } ⇒ self, ... Also known as: filter_values!
Selects hash entries based only on their values, modifying the hash in place
699 700 701 702 703 |
# File 'lib/everythingrb/hash.rb', line 699 def select_values!(&block) return to_enum(:select_values!) if block.nil? select! { |_k, v| block.call(v) } 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.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/everythingrb/hash.rb', line 148 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
175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/everythingrb/hash.rb', line 175 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
229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/everythingrb/hash.rb', line 229 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
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/everythingrb/hash.rb', line 201 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
457 458 459 460 461 |
# File 'lib/everythingrb/hash.rb', line 457 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
479 480 481 482 483 |
# File 'lib/everythingrb/hash.rb', line 479 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.
273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/everythingrb/hash.rb', line 273 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.
312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/everythingrb/hash.rb', line 312 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 |
#value_where {|key, value| ... } ⇒ Object, ...
Returns the first value where the key-value pair satisfies the given condition
504 505 506 507 508 |
# File 'lib/everythingrb/hash.rb', line 504 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
530 531 532 533 534 |
# File 'lib/everythingrb/hash.rb', line 530 def values_where(&block) return to_enum(:values_where) if block.nil? select(&block).values end |