From 576e797838bedf7c136236b1b2a3143da7c607af Mon Sep 17 00:00:00 2001 From: mame Date: Wed, 19 Sep 2018 01:59:26 +0000 Subject: * hash.c (rb_hash_merge): Accepts zero or more hashes as arguments Hash#merge, merge!, and update could merge exactly two hashes. Now, they accepts zero or more hashes as arguments so that it can merge hashes more than two. This patch was created by Koki Ryu at Ruby Hack Challenge #5. Thank you! [ruby-core:88970] [Feature #15111] [Fix GH-1951] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64777 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- hash.c | 94 +++++++++++++++++++++++++++++++++----------------- test/ruby/test_hash.rb | 24 +++++++++++++ 2 files changed, 86 insertions(+), 32 deletions(-) diff --git a/hash.c b/hash.c index 32efd02d5a..8204561bf1 100644 --- a/hash.c +++ b/hash.c @@ -2548,16 +2548,23 @@ rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash) /* * call-seq: - * hsh.merge!(other_hash) -> hsh - * hsh.update(other_hash) -> hsh - * hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh - * hsh.update(other_hash){|key, oldval, newval| block} -> hsh - * - * Adds the contents of _other_hash_ to _hsh_. If no block is specified, - * entries with duplicate keys are overwritten with the values from - * _other_hash_, otherwise the value of each duplicate key is determined by + * hsh.merge!(other_hash1, other_hash2, ...) -> hsh + * hsh.update(other_hash1, other_hash2, ...) -> hsh + * hsh.merge!(other_hash1, other_hash2, ...){|key, oldval, newval| block} + * -> hsh + * hsh.update(other_hash1, other_hash2, ...){|key, oldval, newval| block} + * -> hsh + * + * Adds the contents of _other_hash_s to _hsh_ repeatedly. If no block is + * specified, entries with duplicate keys are overwritten with the values from + * each _other_hash_, otherwise the value of each duplicate key is determined by * calling the block with the key, its value in _hsh_ and its value in - * _other_hash_. + * each _other_hash_. The method also can be called with no argument, + * then nothing will change in the receiver. + * + * h1 = { "a" => 100, "b" => 200 } + * h1.merge!() #=> {"a"=>100, "b"=>200} + * h1 #=> {"a"=>100, "b"=>200} * * h1 = { "a" => 100, "b" => 200 } * h2 = { "b" => 254, "c" => 300 } @@ -2566,23 +2573,36 @@ rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash) * * h1 = { "a" => 100, "b" => 200 } * h2 = { "b" => 254, "c" => 300 } - * h1.merge!(h2) { |key, v1, v2| v1 } - * #=> {"a"=>100, "b"=>200, "c"=>300} - * h1 #=> {"a"=>100, "b"=>200, "c"=>300} + * h3 = { "b" => 100, "d" => 400 } + * h1.merge!(h2, h3) + * #=> {"a"=>100, "b"=>100, "c"=>300, "d"=>400} + * h1 #=> {"a"=>100, "b"=>100, "c"=>300, "d"=>400} + * + * h1 = { "a" => 100, "b" => 200 } + * h2 = { "b" => 254, "c" => 300 } + * h3 = { "b" => 100, "d" => 400 } + * h1.merge!(h2, h3) { |key, v1, v2| v1 } + * #=> {"a"=>100, "b"=>200, "c"=>300, "d"=>400} + * h1 #=> {"a"=>100, "b"=>200, "c"=>300, "d"=>400} */ static VALUE -rb_hash_update(VALUE hash1, VALUE hash2) +rb_hash_update(int argc, VALUE *argv, VALUE self) { - rb_hash_modify(hash1); - hash2 = to_hash(hash2); - if (rb_block_given_p()) { - rb_hash_foreach(hash2, rb_hash_update_block_i, hash1); - } - else { - rb_hash_foreach(hash2, rb_hash_update_i, hash1); + int i; + bool block_given = rb_block_given_p(); + + rb_hash_modify(self); + for (i = 0; i < argc; i++){ + VALUE hash = to_hash(argv[i]); + if (block_given) { + rb_hash_foreach(hash, rb_hash_update_block_i, self); + } + else { + rb_hash_foreach(hash, rb_hash_update_i, self); + } } - return hash1; + return self; } struct update_func_arg { @@ -2641,28 +2661,38 @@ rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func) /* * call-seq: - * hsh.merge(other_hash) -> new_hash - * hsh.merge(other_hash){|key, oldval, newval| block} -> new_hash + * hsh.merge(other_hash1, other_hash2, ...) -> new_hash + * hsh.merge(other_hash1, other_hash2, ...){|key, oldval, newval| block} + * -> new_hash * - * Returns a new hash containing the contents of other_hash and + * Returns a new hash containing the contents of other_hashs and * the contents of hsh. If no block is specified, the value for - * entries with duplicate keys will be that of other_hash. Otherwise - * the value for each duplicate key is determined by calling the block - * with the key, its value in hsh and its value in other_hash. + * entries with duplicate keys will be that of each other_hash. + * Otherwise the value for each duplicate key is determined by calling + * the block with the key, its value in hsh and its value + * in each other_hash. The method also can be called with no argument, + * then a new hash, whose content is same as that of the receiver, + * will be returned; * * h1 = { "a" => 100, "b" => 200 } * h2 = { "b" => 254, "c" => 300 } + * h3 = { "b" => 100, "d" => 400 } + * h1.merge() #=> {"a"=>100, "b"=>200} * h1.merge(h2) #=> {"a"=>100, "b"=>254, "c"=>300} + * h1.merge(h2, h3) + * #=> {"a"=>100, "b"=>100, "c"=>300, "d"=>400} * h1.merge(h2){|key, oldval, newval| newval - oldval} * #=> {"a"=>100, "b"=>54, "c"=>300} + * h1.merge(h2, h3){|key, oldval, newval| newval - oldval} + * #=> {"a"=>100, "b"=>46, "c"=>300, "d"=>400} * h1 #=> {"a"=>100, "b"=>200} * */ static VALUE -rb_hash_merge(VALUE hash1, VALUE hash2) +rb_hash_merge(int argc, VALUE *argv, VALUE self) { - return rb_hash_update(rb_hash_dup(hash1), hash2); + return rb_hash_update(argc, argv, rb_hash_dup(self)); } static int @@ -4761,10 +4791,10 @@ Init_Hash(void) rb_define_method(rb_cHash, "slice", rb_hash_slice, -1); rb_define_method(rb_cHash, "clear", rb_hash_clear, 0); rb_define_method(rb_cHash, "invert", rb_hash_invert, 0); - rb_define_method(rb_cHash, "update", rb_hash_update, 1); + rb_define_method(rb_cHash, "update", rb_hash_update, -1); rb_define_method(rb_cHash, "replace", rb_hash_replace, 1); - rb_define_method(rb_cHash, "merge!", rb_hash_update, 1); - rb_define_method(rb_cHash, "merge", rb_hash_merge, 1); + rb_define_method(rb_cHash, "merge!", rb_hash_update, -1); + rb_define_method(rb_cHash, "merge", rb_hash_merge, -1); rb_define_method(rb_cHash, "assoc", rb_hash_assoc, 1); rb_define_method(rb_cHash, "rassoc", rb_hash_rassoc, 1); rb_define_method(rb_cHash, "flatten", rb_hash_flatten, -1); diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 088178defa..11735c5620 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1123,11 +1123,35 @@ class TestHash < Test::Unit::TestCase assert_equal({1=>6, 3=>4, 5=>7}, h1) end + def test_update3 + h1 = @cls[1=>2, 3=>4] + h1.update() + assert_equal({1=>2, 3=>4}, h1) + h2 = {1=>3, 5=>7} + h3 = {1=>1, 2=>4} + h1.update(h2, h3) + assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1) + end + + def test_update4 + h1 = @cls[1=>2, 3=>4] + h1.update(){|k, v1, v2| k + v1 + v2 } + assert_equal({1=>2, 3=>4}, h1) + h2 = {1=>3, 5=>7} + h3 = {1=>1, 2=>4} + h1.update(h2, h3){|k, v1, v2| k + v1 + v2 } + assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1) + end + def test_merge h1 = @cls[1=>2, 3=>4] h2 = {1=>3, 5=>7} + h3 = {1=>1, 2=>4} + assert_equal({1=>2, 3=>4}, h1.merge()) assert_equal({1=>3, 3=>4, 5=>7}, h1.merge(h2)) assert_equal({1=>6, 3=>4, 5=>7}, h1.merge(h2) {|k, v1, v2| k + v1 + v2 }) + assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3)) + assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3) {|k, v1, v2| k + v1 + v2 }) end def test_assoc -- cgit v1.2.3