1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

hash.c: Add Hash#transform_keys and Hash#transform_keys!

* hash.c (transform_keys_i, rb_hash_transform_keys): Add Hash#transform_keys.
  [Feature #13583] [ruby-core:81290]

* hash.c (rb_hash_transform_keys_bang): Add Hash#transform_keys!.
  [Feature #13583] [ruby-core:81290]

* test/ruby/test_hash.rb: Add tests for above changes.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59328 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
mrkn 2017-07-14 06:44:00 +00:00
parent 0b396d5880
commit 1405111722
2 changed files with 102 additions and 0 deletions

74
hash.c
View file

@ -1824,6 +1824,78 @@ rb_hash_each_pair(VALUE hash)
return hash;
}
static int
transform_keys_i(VALUE key, VALUE value, VALUE result)
{
VALUE new_key = rb_yield(key);
rb_hash_aset(result, new_key, value);
return ST_CONTINUE;
}
/*
* call-seq:
* hsh.transform_keys {|key| block } -> new_hash
* hsh.transform_keys -> an_enumerator
*
* Returns a new hash with the results of running the block once for
* every key.
* This method does not change the values.
*
* h = { a: 1, b: 2, c: 3 }
* h.transform_keys {|k| k.to_s } #=> { "a" => 1, "b" => 2, "c" => 3 }
* h.transform_keys(&:to_s) #=> { "a" => 1, "b" => 2, "c" => 3 }
* h.transform_keys.with_index {|k, i| "#{k}.#{i}" }
* #=> { "a.0" => 1, "b.1" => 2, "c.2" => 3 }
*
* If no block is given, an enumerator is returned instead.
*/
static VALUE
rb_hash_transform_keys(VALUE hash)
{
VALUE result;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
result = rb_hash_new();
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(hash, transform_keys_i, result);
}
return result;
}
/*
* call-seq:
* hsh.transform_keys! {|value| block } -> hsh
* hsh.transform_keys! -> an_enumerator
*
* Invokes the given block once for each key in <i>hsh</i>, replacing it
* with the new key returned by the block, and then returns <i>hsh</i>.
* This method does not change the values.
*
* h = { a: 1, b: 2, c: 3 }
* h.transform_keys! {|k| k.to_s } #=> { "a" => 1, "b" => 2, "c" => 3 }
* h.transform_keys!(&:to_sym) #=> { a: 1, b: 2, c: 3 }
* h.transform_keys!.with_index {|k, i| "#{k}.#{i}" }
* #=> { "a.0" => 1, "b.1" => 2, "c.2" => 3 }
*
* If no block is given, an enumerator is returned instead.
*/
static VALUE
rb_hash_transform_keys_bang(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
if (RHASH(hash)->ntbl) {
long i;
VALUE keys = rb_hash_keys(hash);
for (i = 0; i < RARRAY_LEN(keys); ++i) {
VALUE new_key = rb_yield(RARRAY_AREF(keys, i));
rb_hash_aset(hash, new_key, rb_hash_delete(hash, RARRAY_AREF(keys, i)));
}
}
return hash;
}
static int
transform_values_i(VALUE key, VALUE value, VALUE result)
{
@ -4507,6 +4579,8 @@ Init_Hash(void)
rb_define_method(rb_cHash, "each_pair", rb_hash_each_pair, 0);
rb_define_method(rb_cHash, "each", rb_hash_each_pair, 0);
rb_define_method(rb_cHash, "transform_keys", rb_hash_transform_keys, 0);
rb_define_method(rb_cHash, "transform_keys!", rb_hash_transform_keys_bang, 0);
rb_define_method(rb_cHash, "transform_values", rb_hash_transform_values, 0);
rb_define_method(rb_cHash, "transform_values!", rb_hash_transform_values_bang, 0);

View file

@ -1530,6 +1530,34 @@ class TestHash < Test::Unit::TestCase
assert_equal([10, 20, 30], [1, 2, 3].map(&h))
end
def test_transform_keys
x = @cls[a: 1, b: 2, c: 3]
y = x.transform_keys {|k| :"#{k}!" }
assert_equal({a: 1, b: 2, c: 3}, x)
assert_equal({a!: 1, b!: 2, c!: 3}, y)
enum = x.transform_keys
assert_equal(x.size, enum.size)
assert_instance_of(Enumerator, enum)
y = x.transform_keys.with_index {|k, i| "#{k}.#{i}" }
assert_equal(%w(a.0 b.1 c.2), y.keys)
end
def test_transform_keys_bang
x = @cls[a: 1, b: 2, c: 3]
y = x.transform_keys! {|k| :"#{k}!" }
assert_equal({a!: 1, b!: 2, c!: 3}, x)
assert_same(x, y)
enum = x.transform_keys!
assert_equal(x.size, enum.size)
assert_instance_of(Enumerator, enum)
x.transform_keys!.with_index {|k, i| "#{k}.#{i}" }
assert_equal(%w(a!.0 b!.1 c!.2), x.keys)
end
def test_transform_values
x = @cls[a: 1, b: 2, c: 3]
y = x.transform_values {|v| v ** 2 }