ruby-changes:59490
From: Nobuyoshi <ko1@a...>
Date: Thu, 26 Dec 2019 15:54:04 +0900 (JST)
Subject: [ruby-changes:59490] b25e27277d (master): Transform hash keys by a hash [Feature #16274]
https://git.ruby-lang.org/ruby.git/commit/?id=b25e27277d From b25e27277dc39f25cfca4db8452d254f6cc8046e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada <nobu@r...> Date: Thu, 26 Dec 2019 15:50:34 +0900 Subject: Transform hash keys by a hash [Feature #16274] diff --git a/hash.c b/hash.c index 82383e9..1fd3a33 100644 --- a/hash.c +++ b/hash.c @@ -3023,6 +3023,28 @@ rb_hash_each_pair(VALUE hash) https://github.com/ruby/ruby/blob/trunk/hash.c#L3023 return hash; } +struct transform_keys_args{ + VALUE trans; + VALUE result; + int block_given; +}; + +static int +transform_keys_hash_i(VALUE key, VALUE value, VALUE transarg) +{ + struct transform_keys_args *p = (void *)transarg; + VALUE trans = p->trans, result = p->result; + VALUE new_key = rb_hash_lookup2(trans, key, Qundef); + if (new_key == Qundef) { + if (p->block_given) + new_key = rb_yield(key); + else + new_key = key; + } + rb_hash_aset(result, new_key, value); + return ST_CONTINUE; +} + static int transform_keys_i(VALUE key, VALUE value, VALUE result) { @@ -3049,14 +3071,28 @@ transform_keys_i(VALUE key, VALUE value, VALUE result) https://github.com/ruby/ruby/blob/trunk/hash.c#L3071 * If no block is given, an enumerator is returned instead. */ static VALUE -rb_hash_transform_keys(VALUE hash) +rb_hash_transform_keys(int argc, VALUE *argv, VALUE hash) { VALUE result; + struct transform_keys_args transarg = {0}; - RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size); + argc = rb_check_arity(argc, 0, 1); + if (argc > 0) { + transarg.trans = to_hash(argv[0]); + transarg.block_given = rb_block_given_p(); + } + else { + 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); + if (transarg.trans) { + transarg.result = result; + rb_hash_foreach(hash, transform_keys_hash_i, (VALUE)&transarg); + } + else { + rb_hash_foreach(hash, transform_keys_i, result); + } } return result; @@ -3082,17 +3118,40 @@ static VALUE rb_hash_flatten(int argc, VALUE *argv, VALUE hash); https://github.com/ruby/ruby/blob/trunk/hash.c#L3118 * If no block is given, an enumerator is returned instead. */ static VALUE -rb_hash_transform_keys_bang(VALUE hash) +rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash) { - RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size); + 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 pairs = rb_hash_flatten(0, NULL, hash); rb_hash_clear(hash); for (i = 0; i < RARRAY_LEN(pairs); i += 2) { - VALUE key = RARRAY_AREF(pairs, i), new_key = rb_yield(key), - val = RARRAY_AREF(pairs, i+1); + VALUE key = RARRAY_AREF(pairs, i), new_key, val; + + if (!trans) { + new_key = rb_yield(key); + } + else if ((new_key = rb_hash_lookup2(trans, key, Qundef)) != Qundef) { + /* use the transformed key */ + } + else if (block_given) { + new_key = rb_yield(key); + } + else { + new_key = key; + } + val = RARRAY_AREF(pairs, i+1); rb_hash_aset(hash, new_key, val); } } @@ -6285,8 +6344,8 @@ Init_Hash(void) https://github.com/ruby/ruby/blob/trunk/hash.c#L6344 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_keys", rb_hash_transform_keys, -1); + rb_define_method(rb_cHash, "transform_keys!", rb_hash_transform_keys_bang, -1); 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); diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index ccc3355..733bccd 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1638,6 +1638,9 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L1638 y = x.transform_keys.with_index {|k, i| "#{k}.#{i}" } assert_equal(%w(a.0 b.1 c.2), y.keys) + + assert_equal({A: 1, B: 2, c: 3}, x.transform_keys({a: :A, b: :B, d: :D})) + assert_equal({A: 1, B: 2, "c" => 3}, x.transform_keys({a: :A, b: :B, d: :D}, &:to_s)) end def test_transform_keys_bang @@ -1660,6 +1663,13 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L1663 x = @cls[true => :a, false => :b] x.transform_keys! {|k| !k } assert_equal([false, :a, true, :b], x.flatten) + + x = @cls[a: 1, b: 2, c: 3] + x.transform_keys!({a: :A, b: :B, d: :D}) + assert_equal({A: 1, B: 2, c: 3}, x) + x = @cls[a: 1, b: 2, c: 3] + x.transform_keys!({a: :A, b: :B, d: :D}, &:to_s) + assert_equal({A: 1, B: 2, "c" => 3}, x) end def test_transform_values -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/