Skip to content

Make the number of arguments of Hash#merge variable #1951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from

Conversation

liwii
Copy link

@liwii liwii commented Sep 13, 2018

Abstract

Make the number of arguments of Hash#merge variable.

Background

In many websites such as Stack Overflow and Qiita, many people are seeking how to merge more than three hashes.

https://stackoverflow.com/questions/19548496/how-to-merge-multiple-hashes-in-ruby
https://qiita.com/hc0208/items/c662f5189fa383872f4e
https://stackoverrun.com/ja/q/4997431

Many ways, like using Enumerable#inject or calling Hash#merge multiple times, are proposed, but both don't seem intuitive. Especially when using block in Hash#merge, the code becomes too complicated.

Proposal

Change the argument of Hash#merge from singular to variable length.

Implementation

The code below.

Evaluation

The code to merge more than three hashes became much simpler and more intuitive.

before

hash1.merge(hash2).merge(hash3)

[hash1, hash2, hash3].inject do |result, part|
  result.merge(part) { |key, value1, value2| key + value1 + value2 }
end

after

hash1.merge(hash2, hash3)
hash1.merge(hash2, hash3) { |key, value1, value2| key + value1 + value2 }

Discussion

Summary

The change is needed to make Hash#merge more useful and intuitive.

Copy link
Member

@knu knu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need to update the documentation, and clarify that the method is allowed to be called with zero argument by adding a test case.

hash.c Outdated
if (rb_block_given_p()) {
rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
rb_hash_modify(self);
for(int i = 0; i < argc; i++){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRuby is written in C90, so you should stick to the old syntax here.

int i;

rb_hash_modify(self);
for (i = 0; i < argc; i++) {

hash.c Outdated
rb_hash_modify(self);
for(int i = 0; i < argc; i++){
VALUE hash = to_hash(argv[i]);
if (rb_block_given_p()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can memoize this value.

@knu
Copy link
Member

knu commented Sep 14, 2018

This proposal was accepted in yesterday's meeting, congratulations!

@liwii
Copy link
Author

liwii commented Sep 15, 2018

@knu
Thanks for your review! I modified codes following the review, and added documents and test cases.

hash.c Outdated
int i;

rb_hash_modify(self);
bool block_given = rb_block_given_p();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable declarations are not allowed after a statement.

hash.c Outdated
* More than one hash can be given to this method as arguments, then
* the method returns a new hash containing the contents of the reciever
* itself and all hashes given as arguments. The method also can be called
* with no argument, then the receiver itself will be returned;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result is a new hash, not the receiver itself.

@liwii
Copy link
Author

liwii commented Sep 15, 2018

@nobu
Thanks for your review. I fixed the problems you pointed out.

@marcandre
Copy link
Member

Nice. As far as documentation goes, I'd keep it simple. I don't think that discussing the trivial case of no argument is necessary. The interface should be kept to a minimum by basically changing hsh.merge!(other_hash) to hsh.merge!(other_hash, another_hash, ...). See Array#concat for example.

@liwii
Copy link
Author

liwii commented Sep 16, 2018

@marcandre
Thanks for your review! I fixed documentation and simplified it. I'm not confident in English, so please tell me if you find something wrong in the documentation.

hash.c Outdated
bool block_given = rb_block_given_p();

rb_hash_modify(self);
for(i = 0; i < argc; i++){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation and style in block differ than others.

@@ -1119,15 +1119,40 @@ def test_hash2
def test_update2
h1 = @cls[1=>2, 3=>4]
h2 = {1=>3, 5=>7}
h3 = {1=>1, 2=>4}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is not used AFAIK.

@liwii
Copy link
Author

liwii commented Sep 17, 2018

@nobu
Thanks for review. I didn't notice it. I fixed the indentation in the new commit.

@liwii
Copy link
Author

liwii commented Sep 17, 2018

@simi
Thanks for review. It's completely my mistake. I've already fixed it.

hash.c Outdated
*
* h1 = { "a" => 100, "b" => 200 }
* h2 = { "b" => 254, "c" => 300 }
* h3 = { "b" => 100, "d" => 400 }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed?

@liwii
Copy link
Author

liwii commented Sep 19, 2018

@mame
You are right. It's needless. I fixed it.

@marcandre
Copy link
Member

@marcandre
Thanks for your review! I fixed documentation and simplified it. I'm not confident in English, so please tell me if you find something wrong in the documentation.

Looks good. 👍 I'd even remove the part stating "The method also can be called with no argument, then nothing will change in the receiver.". It's important to test it, but it's not something people will use in general and should be obvious anyways.

@matzbot matzbot closed this in 085f5ef Sep 19, 2018
}
else {
rb_hash_foreach(hash, rb_hash_update_i, self);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very trivial: These lines are indented with 3 spaces from for loop, but maybe usually it's indented with 4 spaces

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thank you.

@mame
Copy link
Member

mame commented Sep 19, 2018

@liwii Merged! Thank you, and I expect you to continue contribution to Ruby 😃
@marcandre Oops, sorry I missed your comment. I agree with you. I've removed the sentence. Thank you.

matzbot pushed a commit that referenced this pull request Sep 19, 2018
As per Marc-Andre's comment.  [Refs GH-1951]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64779 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
@marcandre
Copy link
Member

@marcandre Oops, sorry I missed your comment. I agree with you. I've removed the sentence.

Yes, I commented a few minutes before your commit. Cool, thanks! I added a note in the NEWS.

Congrats @liwii 🎉

morgoth added a commit to freeletics/rails that referenced this pull request Aug 7, 2019
… and `update` methods

It follows Ruby 2.6 addition ruby/ruby#1951
@hsbt hsbt added the Backport label Sep 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants