Feature #15831
closedAdd `Array#extract`, `Hash#extract`, and `ENV.extract`
Description
Add Array#extract
The method removes and returns the elements for which the block returns a true value.
If no block is given, an Enumerator is returned instead.
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = numbers.extract { |number| number.odd? } # => [1, 3, 5, 7, 9]
numbers # => [0, 2, 4, 6, 8]
This method was added to Active Support as extract!
in https://github.com/rails/rails/pull/33137
In this post, you can find use cases of this method
https://bogdanvlviv.com/posts/ruby/rails/array-extract-to-activesupport-6-0.html
Add Hash#extract
The method removes and returns the key/value pairs for which the block evaluates to +true+.
If no block is given, an Enumerator is returned instead.
h = {a: 100, b: 200, c: 300}
h.extract {|k, v| v > 150} # => {:b=>200, :c=>300}
h # => {:a=>100}
Note that there is method extract!
in Active Support that was added in 2009, see
https://github.com/rails/rails/commit/8dcf91ca113579646e95b0fd7a864dfb6512a53b
But I think we should upstream extract!
to Ruby as slice!
.
Add ENV.extract
The method removes and returns the key/value pairs for which the block evaluates to +true+.
If no block is given, an Enumerator is returned instead.
ENV.extract {|k, v| k == "PORT"} # => {"PORT"=>"3000"}
Pull Request: https://github.com/ruby/ruby/pull/2171
Patch: https://patch-diff.githubusercontent.com/raw/ruby/ruby/pull/2171.patch
Updated by shevegen (Robert A. Heiler) over 5 years ago
What is the difference towards e. g. hash.delete()
?
People may ask about this difference. The '!'
too since you can use e. g. Hash #delete
as-is.
Also without '!', I am not sure if extract is a good
name per se, but these are just random comments mostly,
you only have to convince matz, not me. :)
Updated by bogdanvlviv (Bogdan Denkovych) over 5 years ago
- Subject changed from Add `Array#extract!`, `Hash#extract!`, and `ENV::extract!` to Add `Array#extract`, `Hash#extract`, and `ENV::extract`
- Description updated (diff)
Updated by nobu (Nobuyoshi Nakada) over 5 years ago
Why Array#extract
has no argument but takes a block, while Hash
and ENV
are opposite?
Updated by bogdanvlviv (Bogdan Denkovych) over 5 years ago
nobu (Nobuyoshi Nakada) wrote:
Why
Array#extract
has no argument but takes a block, whileHash
andENV
are opposite?
I implemented those methods to mirror similar behavior as it is in Active Support, but I also feel like we need to discuss more about behavior and signature of those methods:
About Array#extract
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = numbers.extract { |number| number.odd? } # => [1, 3, 5, 7, 9]
numbers # => [0, 2, 4, 6, 8]
I do believe that this method should mirror the behavior and the method signature of Array#reject!
https://docs.ruby-lang.org/en/2.6.0/Array.html#method-i-reject-21 since they are similar except one thing - Array#extract
returns elements for which the block returns a true
value. Actually "return elements for the block returns a true
value" is the main reason why I would like to add this method because there could be and there already are lots of use cases for those methods. So I believe this method good as it is.
About Hash#extract
and ENV::extract
First of all, thank you for your question. I also feel Hash#extract
/ENV::extract
should receive a block, in other words should mirror the behavior and the method signature oHash#reject!
, but return the hash with key/value pairs for which the block returns true
. There is an example to make it clear:
h = {a: 100, b: 200, c: 300}
h.extract {|k, v| v > 150} # => {:b=>200, :c=>300}
h # => {:a=>100}
I also feel like we should add Hash#slice!
(since we have Hash#slice
) that would behave exactly in the way I proposed to Hash#extract
initially.
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 # => {}
I would like to work on Hash#slice!
implementation as well.
Let me know what you think about it.
Updated by bogdanvlviv (Bogdan Denkovych) over 5 years ago
- Description updated (diff)
I just changed the implementation of Hash#extract
and ENV::extract
as it's described in the previous note https://bugs.ruby-lang.org/issues/15831#note-4
Updated by bogdanvlviv (Bogdan Denkovych) over 5 years ago
- Description updated (diff)
Updated by bogdanvlviv (Bogdan Denkovych) over 5 years ago
- Subject changed from Add `Array#extract`, `Hash#extract`, and `ENV::extract` to Add `Array#extract`, `Hash#extract`, and `ENV.extract`
- Description updated (diff)
Updated by matz (Yukihiro Matsumoto) over 5 years ago
- Status changed from Open to Rejected
I don't think we have seen the use-case that this method is absolutely necessary. I also concern about the name conflict with the ActiveSupport method.
Let me see the real-world use-case, please.
Matz.
Updated by bogdanvlviv (Bogdan Denkovych) over 5 years ago
matz (Yukihiro Matsumoto) wrote:
I don't think we have seen the use-case that this method is absolutely necessary. I also concern about the name conflict with the ActiveSupport method.
Let me see the real-world use-case, please.
Matz.
There are use-cases of Array#extract
in this Pull Request https://github.com/rails/rails/pull/33137/files
There are also use-cases where Array#extract
would fit in the ruby/ruby codebase:
- default_specs, list = list.partition do |spec|
- spec.default_gem?
- end
+ default_specs = list.extract {|spec| spec.default_gem?}
- default_gems, gems_to_cleanup = gems_to_cleanup.partition do |spec|
- spec.default_gem?
- end
+ default_gems = gems_to_cleanup.extract {|spec| spec.default_gem?}
- filtered, @examples = @examples.partition do |ex|
- ex.filtered?
- end
+ filtered = @examples.extract {|ex| ex.filtered? }
- pre, matches = matches.partition { |spec| spec.version.prerelease? }
+ pre = matches.extract { |spec| spec.version.prerelease? }
- pre, results = results.partition {|spec| spec.version.prerelease? }
+ pre = results.extract {|spec| spec.version.prerelease? }
- suites, rep = rep.partition {|r| r[:testcase] && r[:file] && r[:report].any? {|e| !e[2].is_a?(MiniTest::Skip)}}
+ suites = rep.extract {|r| r[:testcase] && r[:file] && r[:report].any? {|e| !e[2].is_a?(MiniTest::Skip)}}
By the way, numpy/numpy library has similar extract
method: https://www.numpy.org/devdocs/reference/generated/numpy.extract.html?highlight=extract
Updated by bogdanvlviv (Bogdan Denkovych) over 5 years ago
I also concern about the name conflict with the ActiveSupport method.
I guess this comment addressed https://bugs.ruby-lang.org/issues/15863 that adds Hash#slice!
, right?
Active Support doesn't have any extract
methods, only extract!
(with bang).
Updated by bogdanvlviv (Bogdan Denkovych) about 4 years ago
I'm wondering to know whether use-cases, mentioned in https://bugs.ruby-lang.org/issues/15831#note-9, are good enough to reconsider the decision to add these methods to Ruby?
Updated by duerst (Martin Dürst) about 4 years ago
I'm not exactly at ease with the fact that the proposed methods both change the receiver and return a result. But if the names have an '!' at the end, it might be okay.