Project

General

Profile

Feature #15863

Add `Hash#slice!` and `ENV.slice!`

Added by bogdanvlviv (Bogdan Denkovych) 4 months ago. Updated 4 months ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:92722]

Description

Add Hash#slice!

In https://bugs.ruby-lang.org/issues/8499 we added Hash#slice.

Hash#slice! removes and returns the key/value pairs matching the given keys:

h = {a: 100, b: 200, c: 300}
h.slice!(:a) # => {:a=>100}
h # => {:b=>200, :c=>300}
h.slice!(:b, :c, :d) # => {:b=>200, :c=>300}
h # => {}

Note that, this method reflects the behavior of Active Support's
Hash#extract! method that was added in 2009, see
https://github.com/rails/rails/commit/8dcf91ca113579646e95b0fd7a864dfb6512a53b

h = {a: 100, b: 200, c: 300}
h.extract!(:a) # => {:a=>100}
h # => {:b=>200, :c=>300}
h.extract!(:b, :c, :d) # => {:b=>200, :c=>300}
h # => {}

(There is a proposal to add Hash#extract to Ruby - https://bugs.ruby-lang.org/issues/15831, but it has another method signature)

Active Support also has Hash#slice!, see https://api.rubyonrails.org/v5.2/classes/Hash.html#method-i-slice-21.
It is quite different what this patch proposes, see how it works:

h = {a: 100, b: 200, c: 300}
h.slice!(:a) # => {:b=>200, :c=>300} # AS Hash#slice!
h # => {:a=>100}
h.slice!(:b, :c, :d) # => {:a=>100} # AS Hash#slice!
h # => {}

I think Hash#slice! in Ruby should work in the same way as Hash#extract! from Active Support,
there is one argument:

  • https://bugs.ruby-lang.org/issues/8499#note-31 It should behave in the way Hash#slice does, except one thing Hash#slice! modifies the object. (See, for instance, how Array#slice and Array#slice! work, they return the same value)

But I would like to discuss it more to choose the right behavior for the
proposed method. (Maybe there are good arguments why we should add Hash#slice!
with behavior as it is in Active Support)

Add ENV.slice!

The method removes and returns the key/value pairs matching the given keys.

ENV.slice!("PORT", "RAILS_ENV") # => {"PORT"=>"3000", "RAILS_ENV"=>"development"}

Pull Request: https://github.com/ruby/ruby/pull/2195
Patch: https://patch-diff.githubusercontent.com/raw/ruby/ruby/pull/2195.patch


Related issues

Related to Ruby master - Feature #8499: Importing Hash#slice, Hash#slice!, Hash#except, and Hash#except! from ActiveSupportClosedActions

History

#1

Updated by bogdanvlviv (Bogdan Denkovych) 4 months ago

  • Backport deleted (2.4: UNKNOWN, 2.5: UNKNOWN, 2.6: UNKNOWN)
  • Tracker changed from Bug to Feature

Updated by shevegen (Robert A. Heiler) 4 months ago

I do not know how Hash#extract from Active* works but the name does not make to me
a whole lot of sense to me; however had, we have Hash#slice already so I guess it may
make sense to add .slice!; and if so, it may make sense to add it for ENV as
ENV is very hash-like. But I may miss something altogether - this may be best
to actually suggest to the upcoming developer meeting, since it is a fairly small
request/possible change, anyway. (I don't feel I should add it myself because
you suggested it so you should be in control over as to whether you want to
suggest it or not - that is not my decision what you make of course. :))

The next meeting is at:

https://bugs.ruby-lang.org/issues/15782

As for behaviour - this is not necessarily specific to your proposal alone,
but I think ruby users will expect that the trailing "!" indicates a warning
or rather an operation that may be "dangerous" or have side effects - the most
usual one being modifying the receiver in place (or something like that). Not
all methods have a trailing !, for example, replace() on class String. I think
the bottom line is that matz wanted a simple visual cue/indicator for ruby.

So I think whatever the feature request or desired change, I think the most
natural assumption would be that a variant with ! will do precisely the same
as the non-! variant, as action, but without modifying the object at hand.
It is for this reason that I believe that .slice! could (or should) not model
any other behaviour than what .slice() is doing right now. But as said, I may
be missing something; perhaps there was a reason .slice!() was not added,
I would not know.

#3

Updated by nobu (Nobuyoshi Nakada) 4 months ago

  • Related to Feature #8499: Importing Hash#slice, Hash#slice!, Hash#except, and Hash#except! from ActiveSupport added

Updated by mame (Yusuke Endoh) 4 months ago

It should behave in the way Hash#slice does, except one thing Hash#slice! modifies the object. (See, for instance, how Array#slice and Array#slice! work, they return the same value)

I agree that it is ideal. However, in practical, it would be difficult to add to Ruby a feature whose behavior is completely different from the existing feature in Rails.

If you really want to do this, you need first to persuade the developers of ActiveSupport to deprecate (and then remove) the current Hash#slice!, wait for a few years, and finally we can add it to Ruby with a "correct" behavior.

Updated by matz (Yukihiro Matsumoto) 4 months ago

  • Status changed from Open to Rejected

I don't think we have seen the use-case that this method is absolutely necessary.
Let me see the real-world use-case, please.

Matz.

Updated by bogdanvlviv (Bogdan Denkovych) 4 months ago

Let me see the real-world use-case, please.

Since proposed Hash#slice! method is the same as Hash#extract! from the Active Support, there is one use-case where we have some options then we extract some known by keys options and assign it to path_options variable to be able to use them later in one way and the rest options to use in another way:
https://github.com/rails/rails/blob/c1e949e9e618f75dc446ffa584c3b441c48714b1/actionview/lib/action_view/helpers/asset_tag_helper.rb#L89

Also available in: Atom PDF