[ruby-core:119168] [Ruby master Feature#20738] Removing a specific entry from a hash literal

Issue #20738 has been reported by ursm (Keita Urashima). ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by nobu (Nobuyoshi Nakada). "A special value" doesn't feel like a good idea to me. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109748 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by osyo (manga osyo). How about using `**`? ```ruby def bar? = false { foo: 1, **(bar? ? { bar: 2 } : {}) } # => {:foo=>1} ``` Also, you can use `**nil` in ruby 3.4-dev. ```ruby def bar? = false { foo: 1, **({ bar: 2 } if bar?) } # => {:foo=>1} ``` see: * [Feature #18959: Handle gracefully nil kwargs eg. **nil - Ruby master - Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/18959) * [Bug #20064: Inconsistent behavior between array splat *nil and hash splat **nil - Ruby master - Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/20064) ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109750 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). Yes, I sometimes do that as well. However, I am not happy that the shape of the resulting hash is unclear. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109751 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). nobu (Nobuyoshi Nakada) wrote in #note-1:
"A special value" doesn't feel like a good idea to me.
Hmmm, does that mean we should extend the syntax? For example, something like this? ``` ruby { foo: 1, ?bar: nil } #=> {foo: 1} ``` ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109752 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). With the previous idea, I can't have both removing entries and returning nil depending on the condition. ``` ruby # If user.child? is true and user.parent is nil, I want parent_name: nil, but the entry is removed. { ?parent_name: user.child? ? user.parent&.name : nil } ``` ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109753 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by mame (Yusuke Endoh). History. Long ago in Ruby, such a special value actually existed. It was `nil`. ```ruby # ruby 1.4.6 h = { 1 => 2 } p h #=> {1=>2} # This removes the key 1 h[1] = nil p h #=> {} ``` However, there were more and more cases where we want to treat `nil` as an ordinary value. Finally, `nil` has lost this speciality. Considering this history, the special value is not a good idea. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109773 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). I believe that the following two points will prevent the same problems as in the past: 1. Use a value that is never used (e.g., Hash::DROP) instead of nil. 2. Special treatment of “special value” only if the hash is constructed with literals. ``` ruby { foo: Hash::DROP } #=> {} h = {} h[:foo] = Hash::DROP h #=> {foo: Hash::DROP} ``` Note that I am not concerned with the “special value” approach. If there is a better way, please let me know. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109774 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by Eregon (Benoit Daloze). I don't think it's OK to magically drop entries from a literal based on some value, it's way too surprising. And of course `Hash::DROP` could leak into some variable and then unintentionally drop an entry, that'd be horrible to debug. Notably this would make the capacity of the literal potentially wrong, etc. ```ruby { foo: 1, **({ bar: 2 } if bar?) } # OR h[:bar] = 2 if bar? ``` Sounds good enough to me. I don't think it's very frequent to need this to warrant a syntax change either. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109776 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). Eregon (Benoit Daloze) wrote in #note-8:
I don't think it's very frequent to need this to warrant a syntax change either.
There are several Rails codes that can be improved with this feature. In my opinion, it is especially useful in multiple situations, such as generating JSON responses and constructing HTTP request headers. It would be more useful if it could be applied to keyword arguments as well. - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109781 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by jeremyevans0 (Jeremy Evans). ursm (Keita Urashima) wrote in #note-9:
There are several Rails codes that can be improved with this feature. In my opinion, it is especially useful in multiple situations, such as generating JSON responses and constructing HTTP request headers. It would be more useful if it could be applied to keyword arguments as well.
- https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3... - https://github.com/rails/rails/blob/6d1252cf3e65a7720aad5511ff719b44e49fd2a3...
I reviewed every example listed and in all cases I think it would be made harder to understand using the proposed feature. Each example listed is straightforward and easy to understand currently. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109782 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by byroot (Jean Boussier).
There are several Rails codes that can be improved with this feature.
As one of the maintainer of the code you linked to, I agree with @jeremyevans0 that it's currently fine as it is. I wouldn't use such feature if it was added. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109784 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). I would like to offer that as I used a simple grep pattern, I could only find simple examples. I wanted to show that this is not as rare as it seems. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109787 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). It would be better to explain the motive. This is an appropriate code. ``` ruby { foo: 1 bar: 2 } ``` This is not a mistake, but it's a circuitous code. ``` ruby h = {} h[:foo] = 1 h[:bar] = 2 h ``` The gist of this proposal is that if a condition is included, it should be written in the same form as the “appropriate code”. ``` ruby h = {} h[:foo] = 1 h[:bar] = 2 if bar? h ``` ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-109788 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by matz (Yukihiro Matsumoto). I don't want to add a special value (Hash::DROP) nor special syntax (?key:) here. Use `h = {foo: 1}; h[:bar] = 2 if bar?`. Matz. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-110036 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/

Issue #20738 has been updated by ursm (Keita Urashima). OK, I'm sorry to hear that, but I'm glad to hear your opinions. Thanks. ---------------------------------------- Feature #20738: Removing a specific entry from a hash literal https://bugs.ruby-lang.org/issues/20738#change-110101 * Author: ursm (Keita Urashima) * Status: Open ---------------------------------------- Sometimes I want to decide whether or not to add a particular entry to a hash depending on a condition. If the entire hash does not use nil, I can use Hash#compact. ```ruby { foo: 1, bar: bar? ? 2 : nil }.compact ``` But if I want to remove only a specific entry while leaving the other nil, it is somewhat cumbersome. I have to either assign the hash once and change it destructively, or use Hash#reject. ``` ruby h = { foo: 1, baz: nil } h[:bar] = 2 if bar? ``` ``` ruby { foo: 1, bar: bar? ? 2 : :drop, baz: nil }.reject {|_, v| v == :drop } ``` As a suggestion, how about a special value that indicates an invalid key for the hash? With this, the above example could be written like this: ``` ruby { foo: 1, bar: bar? ? 2 : Hash::DROP, baz: nil } #=> {foo: 1, baz: nil} ``` -- https://bugs.ruby-lang.org/
participants (8)
-
byroot (Jean Boussier)
-
Eregon (Benoit Daloze)
-
jeremyevans0 (Jeremy Evans)
-
mame (Yusuke Endoh)
-
matz (Yukihiro Matsumoto)
-
nobu (Nobuyoshi Nakada)
-
osyo (manga osyo)
-
ursm (Keita Urashima)