Hash#transform_keys!
static VALUE
rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
{
VALUE trans = 0;
int block_given = 0;
argc = rb_check_arity(argc, 0, 1);
if (argc > 0) {
trans = to_hash(argv[0]);
block_given = rb_block_given_p();
}
else {
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
}
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
long i;
VALUE new_keys = hash_alloc(0);
VALUE pairs = rb_ary_hidden_new(RHASH_SIZE(hash) * 2);
rb_hash_foreach(hash, flatten_i, pairs);
for (i = 0; i < RARRAY_LEN(pairs); i += 2) {
VALUE key = RARRAY_AREF(pairs, i), new_key, val;
if (!trans) {
new_key = rb_yield(key);
}
else if (!UNDEF_P(new_key = rb_hash_lookup2(trans, key, Qundef))) {
/* use the transformed key */
}
else if (block_given) {
new_key = rb_yield(key);
}
else {
new_key = key;
}
val = RARRAY_AREF(pairs, i+1);
if (!hash_stlike_lookup(new_keys, key, NULL)) {
rb_hash_stlike_delete(hash, &key, NULL);
}
rb_hash_aset(hash, new_key, val);
rb_hash_aset(new_keys, new_key, Qnil);
}
rb_ary_clear(pairs);
rb_hash_clear(new_keys);
}
compact_after_delete(hash);
return hash;
}
With an argument, a block, or both given, derives keys from the argument, the block, and self; all, some, or none of the keys in self may be changed.
With a block given and no argument, derives keys only from the block; all, some, or none of the keys in self may be changed.
For each key/value pair old_key/value in self, calls the block with old_key; the block’s return value becomes new_key; removes the entry for old_key: self.delete(old_key); sets self[new_key] = value; a duplicate key overwrites:
h = {foo: 0, bar: 1, baz: 2} h.transform_keys! {|old_key| old_key.to_s } # => {"foo" => 0, "bar" => 1, "baz" => 2} h = {foo: 0, bar: 1, baz: 2} h.transform_keys! {|old_key| 'xxx' } # => {"xxx" => 2}
With argument other_hash given and no block, derives keys for self from other_hash and self; all, some, or none of the keys in self may be changed.
For each key/value pair old_key/old_value in self, looks for key old_key in other_hash:
-
If
old_keyis found, takes valueother_hash[old_key]asnew_key; removes the entry forold_key:self.delete(old_key); setsself[new_key] = value; a duplicate key overwrites:Exampleh = {foo: 0, bar: 1, baz: 2} h.transform_keys!(baz: :BAZ, bar: :BAR, foo: :FOO) # => {FOO: 0, BAR: 1, BAZ: 2} h = {foo: 0, bar: 1, baz: 2} h.transform_keys!(baz: :FOO, bar: :FOO, foo: :FOO) # => {FOO: 2}
-
If
old_keyis not found, does nothing:Exampleh = {foo: 0, bar: 1, baz: 2} h.transform_keys!({}) # => {foo: 0, bar: 1, baz: 2} h.transform_keys!(baz: :foo) # => {foo: 2, bar: 1}
Unused keys in other_hash are ignored:
h = {foo: 0, bar: 1, baz: 2} h.transform_keys!(bat: 3) # => {foo: 0, bar: 1, baz: 2}
With both argument other_hash and a block given, derives keys from other_hash, the block, and self; all, some, or none of the keys in self may be changed.
For each pair old_key and value in self:
-
If
other_hashhas keyold_key(with valuenew_key), does not call the block for that key; removes the entry forold_key:self.delete(old_key); setsself[new_key] = value; a duplicate key overwrites:Exampleh = {foo: 0, bar: 1, baz: 2} h.transform_keys!(baz: :BAZ, bar: :BAR, foo: :FOO) {|key| fail 'Not called' } # => {FOO: 0, BAR: 1, BAZ: 2}
-
If
other_hashdoes not have keyold_key, calls the block withold_keyand takes its return value asnew_key; removes the entry forold_key:self.delete(old_key); setsself[new_key] = value; a duplicate key overwrites:Exampleh = {foo: 0, bar: 1, baz: 2} h.transform_keys!(baz: :BAZ) {|key| key.to_s.reverse } # => {"oof" => 0, "rab" => 1, BAZ: 2} h = {foo: 0, bar: 1, baz: 2} h.transform_keys!(baz: :BAZ) {|key| 'ook' } # => {"ook" => 1, BAZ: 2}
With no argument and no block given, returns a new Enumerator.
Related: see Methods for Transforming Keys and Values.
Ruby version changes
-
3.0
-
Hash#transform_keysandHash#transform_keys!now accept a hash that maps keys to new keys. [Feature #16274]
-