Class: Hash

Inherits:
Object
  • Object
show all
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)

Examples:

require "everythingrb/hash"
config = {server: {port: 443}}.to_ostruct
config.server.port  # => 443

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.

Returns:

  • (Struct)

    A struct with a single nil-valued field

Struct.new(:_).new(nil)

Instance Method Summary collapse

Methods included from Everythingrb::InspectQuotable

#in_quotes

Instance Method Details

#compact_blank_merge(other = {}) ⇒ Hash

Note:

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.).

Examples:

Building API responses without blank values

user_data = {name: "Alice", role: "admin"}
user_data.compact_blank_merge(
  bio: "",           # excluded - blank string
  tags: [],          # excluded - empty array
  email: "a@example.com", # included - present
  phone: nil         # excluded - nil
)
# => {name: "Alice", role: "admin", email: "a@example.com"}

Configuration merging with blank filtering

defaults = {timeout: 30, retries: 3}
user_config = {timeout: "", retries: 5, debug: true}
defaults.compact_blank_merge(user_config)
# => {timeout: 30, retries: 5, debug: true}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Returns:

  • (Hash)

    A new hash with present values merged



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

Note:

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.).

Examples:

Building parameters while excluding blanks

params = {action: "search", format: "json"}
params.compact_blank_merge!(
  query: "",       # excluded - blank
  page: 2,         # included - present
  tags: []         # excluded - empty array
)
# => {action: "search", format: "json", page: 2}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Returns:

  • (self)

    The modified hash



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.

Examples:

Merge only non-nil values (common when building parameters)

user_id = 42
email = nil
name = "Alice"

{}.compact_merge(
  id: user_id,
  email: email,
  name: name
)
# => {id: 42, name: "Alice"}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Returns:

  • (Hash)

    A new hash with non-nil values merged



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.

Examples:

Merge only non-nil values in place

params = {format: "json"}
params.compact_merge!(
  page: 1,
  per_page: nil,
  sort: "created_at"
)
# => {format: "json", page: 1, sort: "created_at"}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Returns:

  • (self)

    The modified hash



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.

Examples:

Basic usage

hash = {a: 1, b: {c: 2, d: [3, 4]}}
hash.deep_transform_values { |v| v * 2 }
# => {a: 2, b: {c: 4, d: [6, 8]}}

With key access

hash = {a: 1, b: {c: 2}}
hash.deep_transform_values(with_key: true) { |v, k| "#{k}:#{v}" }
# => {a: "a:1", b: {c: "c:2"}}

Parameters:

  • with_key (Boolean) (defaults to: false)

    Whether to yield both value and key to the block

Yields:

  • (value, key)

    Block that transforms each value

Yield Parameters:

  • value (Object)

    The value to transform

  • key (Object)

    The corresponding key (only if with_key: true)

Yield Returns:

  • (Object)

    The transformed value

Returns:

  • (Hash)

    A new hash with all values recursively transformed

  • (Enumerator)

    If no block is given



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.

Examples:

Basic usage

hash = {a: 1, b: {c: 2, d: [3, 4]}}
hash.deep_transform_values! { |v| v * 2 }
# => {a: 2, b: {c: 4, d: [6, 8]}}
# hash is now {a: 2, b: {c: 4, d: [6, 8]}}

With key access

hash = {a: 1, b: {c: 2}}
hash.deep_transform_values!(with_key: true) { |v, k| "#{k}:#{v}" }
# => {a: "a:1", b: {c: "c:2"}}
# hash is now {a: "a:1", b: {c: "c:2"}}

Parameters:

  • with_key (Boolean) (defaults to: false)

    Whether to yield both value and key to the block

Yields:

  • (value, key)

    Block that transforms each value

Yield Parameters:

  • value (Object)

    The value to transform

  • key (Object)

    The corresponding key (only if with_key: true)

Yield Returns:

  • (Object)

    The transformed value

Returns:

  • (self)

    The transformed hash

  • (Enumerator)

    If no block is given



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

Examples:

Find first admin user by role

users = {
  alice: {name: "Alice", role: "admin"},
  bob: {name: "Bob", role: "user"},
  charlie: {name: "Charlie", role: "admin"}
}
users.find_value { |k, v| v[:role] == "admin" } # => {name: "Alice", role: "admin"}

Yields:

  • (key, value)

    Block that determines whether to include the value

Yield Parameters:

  • key (Object)

    The current key

  • value (Object)

    The current value

Yield Returns:

  • (Boolean)

    Whether to include this value

Returns:

  • (Object, nil)

    The first matching value or nil if none found

  • (Enumerator)

    If no block is given



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

Examples:

Basic usage without index

{ a: 1, b: nil, c: 2, d: nil, e: 3 }.join_map(", ") { |k, v| "#{k}-#{v}" if v }
# => "a-1, c-2, e-3"

With index for position-aware formatting

users = {alice: "Alice", bob: "Bob", charlie: "Charlie"}
users.join_map(", ", with_index: true) do |(k, v), i|
  "#{i + 1}. #{v}"
end
# => "1. Alice, 2. Bob, 3. Charlie"

Without a block (default behavior)

{ a: 1, b: nil, c: 2 }.join_map(" ")
# => "a 1 b c 2"

Parameters:

  • join_with (String) (defaults to: "")

    The delimiter to join elements with (defaults to empty string)

  • with_index (Boolean) (defaults to: false)

    Whether to include the index in the block (defaults to false)

Yields:

  • (key_value_pair, index)

    Block that filters and transforms hash entries

Yield Parameters:

  • key_value_pair (Array)

    Two-element array containing [key, value] (can be destructured as |key, value|)

  • index (Integer)

    The index of the current entry (only if with_index: true)

Returns:

  • (String)

    Joined string of filtered and transformed entries



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

Examples:

Merge only even-numbered keys

{a: 1, b: 2}.merge_if(c: 3, d: 4) { |key, _| key.to_s.ord.even? }
# => {a: 1, b: 2, d: 4}

Merge only positive values

{a: 1, b: 2}.merge_if(c: 3, d: -4) { |_, value| value > 0 }
# => {a: 1, b: 2, c: 3}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Yields:

  • (key, value)

    Block that determines whether to include each key-value pair

Yield Parameters:

  • key (Object)

    The key from the other hash

  • value (Object)

    The value from the other hash

Yield Returns:

  • (Boolean)

    Whether to include this key-value pair

Returns:

  • (Hash)

    A new hash with conditionally merged key-value pairs



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

Examples:

Merge only even-numbered keys in place

hash = {a: 1, b: 2}
hash.merge_if!(c: 3, d: 4) { |key, _| key.to_s.ord.even? }
# => {a: 1, b: 2, d: 4}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Yields:

  • (key, value)

    Block that determines whether to include each key-value pair

Yield Parameters:

  • key (Object)

    The key from the other hash

  • value (Object)

    The value from the other hash

Yield Returns:

  • (Boolean)

    Whether to include this key-value pair

Returns:

  • (self)

    The modified hash



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

Examples:

Merge only string values

{a: 1, b: "old"}.merge_if_values(c: "new", d: 2) { |v| v.is_a?(String) }
# => {a: 1, b: "old", c: "new"}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Yields:

  • (value)

    Block that determines whether to include each value

Yield Parameters:

  • value (Object)

    The value from the other hash

Yield Returns:

  • (Boolean)

    Whether to include this value

Returns:

  • (Hash)

    A new hash with conditionally merged 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

Examples:

Merge only numeric values in place

hash = {a: 1, b: "text"}
hash.merge_if_values!(c: "ignore", d: 2) { |v| v.is_a?(Numeric) }
# => {a: 1, b: "text", d: 2}

Parameters:

  • other (Hash) (defaults to: {})

    The hash to merge from

Yields:

  • (value)

    Block that determines whether to include each value

Yield Parameters:

  • value (Object)

    The value from the other hash

Yield Returns:

  • (Boolean)

    Whether to include this value

Returns:

  • (self)

    The modified hash



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

Examples:

Renames a single key

{a: 1, b: 2, c: 3}.rename_key(:b, :middle)
# => {a: 1, middle: 2, c: 3}

Parameters:

  • old_key (Object)

    The key to rename

  • new_key (Object)

    The new key to use

Returns:

  • (Hash)

    A new hash with the key renamed



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

Examples:

Renames a key in place

hash = {a: 1, b: 2, c: 3}
hash.rename_key!(:b, :middle)
# => {a: 1, middle: 2, c: 3}

Parameters:

  • old_key (Object)

    The key to rename

  • new_key (Object)

    The new key to use

Returns:

  • (self)

    The modified hash



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.

Examples:

Rename a single key without preserving order

{a: 1, b: 2, c: 3}.rename_key_unordered(:b, :middle)
# => {a: 1, c: 3, middle: 2}  # Order may differ

Parameters:

  • old_key (Object)

    The key to rename

  • new_key (Object)

    The new key to use

Returns:

  • (Hash)

    A new hash with the key renamed



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.

Examples:

Rename a key in place without preserving order

hash = {a: 1, b: 2, c: 3}
hash.rename_key_unordered!(:b, :middle)
# => {a: 1, c: 3, middle: 2}  # Order may differ

Parameters:

  • old_key (Object)

    The key to rename

  • new_key (Object)

    The new key to use

Returns:

  • (self)

    The modified hash



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.

Examples:

Renames multiple keys

{a: 1, b: 2, c: 3, d: 4}.rename_keys(a: :first, c: :third)
# => {first: 1, b: 2, third: 3, d: 4}

Parameters:

  • keys (Hash)

    A mapping of old_key => new_key pairs

Returns:

  • (Hash)

    A new hash with keys renamed



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.

Examples:

Rename multiple keys in place

hash = {a: 1, b: 2, c: 3, d: 4}
hash.rename_keys!(a: :first, c: :third)
# => {first: 1, b: 2, third: 3, d: 4}

Parameters:

  • keys (Hash)

    A mapping of old_key => new_key pairs

Returns:

  • (self)

    The modified hash



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

Examples:

Find all admin users by role

users = {
  alice: {name: "Alice", role: "admin"},
  bob: {name: "Bob", role: "user"},
  charlie: {name: "Charlie", role: "admin"}
}
users.select_values { |k, v| v[:role] == "admin" }
# => [{name: "Alice", role: "admin"}, {name: "Charlie", role: "admin"}]

Yields:

  • (key, value)

    Block that determines whether to include the value

Yield Parameters:

  • key (Object)

    The current key

  • value (Object)

    The current value

Yield Returns:

  • (Boolean)

    Whether to include this value

Returns:

  • (Array)

    All matching values

  • (Enumerator)

    If no block is given



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_datumDatum

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.

Examples:

hash = { person: { name: "Bob", age: 30 } }
data = hash.to_datum
data.person.name # => "Bob"

Returns:

  • (Datum)

    An immutable object mirroring the hash structure



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_istructDatum

Deprecated.

Use #to_datum instead. Will be removed in v2.0.0.

Recursively converts hash to an immutable Data structure

Examples:

hash = { person: { name: "Bob", age: 30 } }
data = hash.to_istruct
data.person.name # => "Bob"

Returns:

  • (Datum)

    An immutable object mirroring the hash 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_ostructOpenStruct

Converts hash to an OpenStruct recursively

Examples:

hash = { config: { api_key: "secret" } }
config = hash.to_ostruct
config.config.api_key # => "secret"

Returns:

  • (OpenStruct)

    An OpenStruct with methods matching hash keys



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_structStruct

Converts hash to a Struct recursively

Examples:

hash = { user: { name: "Alice", roles: ["admin"] } }
struct = hash.to_struct
struct.user.name # => "Alice"
struct.class # => Struct

Returns:

  • (Struct)

    A struct with methods matching hash keys



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

Examples:

Transform both keys and values

{a: 1, b: 2}.transform { |k, v| ["#{k}_key", v * 2] }
# => {a_key: 2, b_key: 4}

Yields:

  • (key, value)

    Block that returns a new key-value pair

Yield Parameters:

  • key (Object)

    The original key

  • value (Object)

    The original value

Yield Returns:

  • (Array)

    Two-element array containing the new key and value

Returns:

  • (Hash)

    A new hash with transformed keys and values

  • (Enumerator)

    If no block is given



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

Examples:

Transform both keys and values in place

hash = {a: 1, b: 2}
hash.transform! { |k, v| ["#{k}_key", v * 2] }
# => {a_key: 2, b_key: 4}

Yields:

  • (key, value)

    Block that returns a new key-value pair

Yield Parameters:

  • key (Object)

    The original key

  • value (Object)

    The original value

Yield Returns:

  • (Array)

    Two-element array containing the new key and value

Returns:

  • (self)

    The transformed hash

  • (Enumerator)

    If no block is given



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.

Examples:

Standard usage

{a: 1, b: 2}.transform_values { |v| v * 2 }
# => {a: 2, b: 4}

With key access

{a: 1, b: 2}.transform_values(with_key: true) { |v, k| "#{k}:#{v}" }
# => {a: "a:1", b: "b:2"}

Parameters:

  • with_key (Boolean) (defaults to: false)

    Whether to yield both value and key to the block

Yields:

  • (value, key)

    Block that transforms each value

Yield Parameters:

  • value (Object)

    The value to transform

  • key (Object)

    The corresponding key (only if with_key: true)

Yield Returns:

  • (Object)

    The transformed value

Returns:

  • (Hash)

    A new hash with transformed values

  • (Enumerator)

    If no block is given



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.

Examples:

Standard usage

hash = {a: 1, b: 2}
hash.transform_values! { |v| v * 2 }
# => {a: 2, b: 4}
# hash is now {a: 2, b: 4}

With key access

hash = {a: 1, b: 2}
hash.transform_values!(with_key: true) { |v, k| "#{k}:#{v}" }
# => {a: "a:1", b: "b:2"}
# hash is now {a: "a:1", b: "b:2"}

Parameters:

  • with_key (Boolean) (defaults to: false)

    Whether to yield both value and key to the block

Yields:

  • (value, key)

    Block that transforms each value

Yield Parameters:

  • value (Object)

    The value to transform

  • key (Object)

    The corresponding key (only if with_key: true)

Yield Returns:

  • (Object)

    The transformed value

Returns:

  • (self)

    The transformed hash

  • (Enumerator)

    If no block is given



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