ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output
@ 2020-03-24 21:00 jacobevelyn
  2020-03-25  3:31 ` [ruby-core:97590] " sawadatsuyoshi
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: jacobevelyn @ 2020-03-24 21:00 UTC (permalink / raw
  To: ruby-core

Issue #16739 has been reported by jacobevelyn (Jacob Evelyn).

----------------------------------------
Feature #16739: Allow Hash#keys and Hash#values to accept a block for filtering output
https://bugs.ruby-lang.org/issues/16739

* Author: jacobevelyn (Jacob Evelyn)
* Status: Open
* Priority: Normal
----------------------------------------
I often see code like the following:

``` ruby
hash.select { |_, v| v == :some_value }.keys
hash.keys.select { |k| k.nil? || k.even? }
hash.select { |k, v| valid_key?(k) && valid_value?(v) }.values
```

Each of these code snippets must allocate an intermediate data structure. I propose allowing `Hash#keys` and `Hash#values` to accept optional block parameters that *take both key and value*. For example, the above code could be rewritten as:

```ruby
hash.keys { |_, v| v == :some_value }
hash.keys { |k, _| k.nil? || k.even? }
hash.values { |k, v| valid_key?(k) && valid_value?(v) }
```

This behavior:

1. Does not break any existing code (since `Hash#keys` and `Hash#values` do not currently accept blocks).
2. Is very readable—it's obvious what it does at a glance.
3. Is more efficient than current alternatives.
4. Is more concise than current alternatives.
5. Is flexible and useful in a variety of scenarios, because the block has access to both key and value (unlike the behavior proposed in #14788).



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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:97590] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output
  2020-03-24 21:00 [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output jacobevelyn
@ 2020-03-25  3:31 ` sawadatsuyoshi
  2020-03-25 15:14 ` [ruby-core:97595] " mame
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: sawadatsuyoshi @ 2020-03-25  3:31 UTC (permalink / raw
  To: ruby-core

Issue #16739 has been updated by sawa (Tsuyoshi Sawada).


All it does is saves you from typing `select`. It does not look like the proposed feature makes much difference unless such situation is frequently met. Do you have use case?

----------------------------------------
Feature #16739: Allow Hash#keys and Hash#values to accept a block for filtering output
https://bugs.ruby-lang.org/issues/16739#change-84773

* Author: jacobevelyn (Jacob Evelyn)
* Status: Open
* Priority: Normal
----------------------------------------
I often see code like the following:

``` ruby
hash.select { |_, v| v == :some_value }.keys
hash.keys.select { |k| k.nil? || k.even? }
hash.select { |k, v| valid_key?(k) && valid_value?(v) }.values
```

Each of these code snippets must allocate an intermediate data structure. I propose allowing `Hash#keys` and `Hash#values` to accept optional block parameters that *take both key and value*. For example, the above code could be rewritten as:

```ruby
hash.keys { |_, v| v == :some_value }
hash.keys { |k, _| k.nil? || k.even? }
hash.values { |k, v| valid_key?(k) && valid_value?(v) }
```

This behavior:

1. Does not break any existing code (since `Hash#keys` and `Hash#values` do not currently accept blocks).
2. Is very readable—it's obvious what it does at a glance.
3. Is more efficient than current alternatives.
4. Is more concise than current alternatives.
5. Is flexible and useful in a variety of scenarios, because the block has access to both key and value (unlike the behavior proposed in #14788).



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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:97595] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output
  2020-03-24 21:00 [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output jacobevelyn
  2020-03-25  3:31 ` [ruby-core:97590] " sawadatsuyoshi
@ 2020-03-25 15:14 ` mame
  2020-03-25 15:31 ` [ruby-core:97596] " jacobevelyn
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: mame @ 2020-03-25 15:14 UTC (permalink / raw
  To: ruby-core

Issue #16739 has been updated by mame (Yusuke Endoh).


I doubt if it is obvious.  See the following code.  I believe that many people expect `.map`.

```ruby
hash.keys {|k| k.to_s }
```

If you want to avoid an intermediate array, you may want to use `.each_key` or `.filter_map`.

```ruby
hash.each_key.select {|k| k.nil? || k.even? }
hash.filter_map {|k, v| k if valid_key?(k) && valid_value?(v) }
```

Personally I like the following explicit code, though.

```ruby
keys = []
hash.each do |k, v|
  keys << k if valid_key?(k) && valid_value?(v)
end
```

----------------------------------------
Feature #16739: Allow Hash#keys and Hash#values to accept a block for filtering output
https://bugs.ruby-lang.org/issues/16739#change-84779

* Author: jacobevelyn (Jacob Evelyn)
* Status: Open
* Priority: Normal
----------------------------------------
I often see code like the following:

``` ruby
hash.select { |_, v| v == :some_value }.keys
hash.keys.select { |k| k.nil? || k.even? }
hash.select { |k, v| valid_key?(k) && valid_value?(v) }.values
```

Each of these code snippets must allocate an intermediate data structure. I propose allowing `Hash#keys` and `Hash#values` to accept optional block parameters that *take both key and value*. For example, the above code could be rewritten as:

```ruby
hash.keys { |_, v| v == :some_value }
hash.keys { |k, _| k.nil? || k.even? }
hash.values { |k, v| valid_key?(k) && valid_value?(v) }
```

This behavior:

1. Does not break any existing code (since `Hash#keys` and `Hash#values` do not currently accept blocks).
2. Is very readable—it's obvious what it does at a glance.
3. Is more efficient than current alternatives.
4. Is more concise than current alternatives.
5. Is flexible and useful in a variety of scenarios, because the block has access to both key and value (unlike the behavior proposed in #14788).



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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:97596] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output
  2020-03-24 21:00 [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output jacobevelyn
  2020-03-25  3:31 ` [ruby-core:97590] " sawadatsuyoshi
  2020-03-25 15:14 ` [ruby-core:97595] " mame
@ 2020-03-25 15:31 ` jacobevelyn
  2020-03-25 18:28 ` [ruby-core:97597] " eregontp
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: jacobevelyn @ 2020-03-25 15:31 UTC (permalink / raw
  To: ruby-core

Issue #16739 has been updated by jacobevelyn (Jacob Evelyn).


> All it does is saves you from typing `select`. It does not look like the proposed feature makes much difference unless such situation is frequently met. Do you have any use case?

I see code that could be improved with this all the time, including in projects like [Ruby](https://github.com/ruby/ruby/blob/master/lib/reline/key_stroke.rb#L19-L35), [JRuby](https://github.com/jruby/jruby/blob/master/lib/ruby/stdlib/ffi/enum.rb#L247-L283), [Rails](https://github.com/rails/rails/blob/master/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb#L38), [Discourse](https://github.com/discourse/discourse/blob/master/lib/stylesheet/manager.rb#L22-L30), [GitLab](https://github.com/gitlabhq/gitlabhq/blob/master/app/models/repository.rb#L919), [Metasploit](https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/scanner/snmp/snmp_enumusers.rb#L41), [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/resource.rb#L191-L195), and [Airbnb's Nerve](https://github.com/airbnb/nerve/blob/master/lib/nerve.rb#L100-L115) (see links).

As I mentioned in my original post, it does not just save you from typing `select`—it also *avoids an unnecessary Hash allocation*, making it **more efficient** as well as more concise.

> If you want to avoid an intermediate array, you may want to use `.each_key` or `.filter_map`.

Many of these use cases aren't supported by `.each_key` or `.each_value` because they require looking at the one that's not being returned (or both keys and values). Honestly I wasn't aware of `.filter_map`; you're right that it's an option but I find it a bit verbose and hard to read.

> the purpose of a block following the methods keys and values does not seem to be immediately clear.

Obviously we can disagree about this. I think about it like `Enumerable#count`, where *what the method returns* does not change (an `Integer`) but the method becomes more flexible and powerful when you use a block.

----------------------------------------
Feature #16739: Allow Hash#keys and Hash#values to accept a block for filtering output
https://bugs.ruby-lang.org/issues/16739#change-84780

* Author: jacobevelyn (Jacob Evelyn)
* Status: Open
* Priority: Normal
----------------------------------------
I often see code like the following:

``` ruby
hash.select { |_, v| v == :some_value }.keys
hash.keys.select { |k| k.nil? || k.even? }
hash.select { |k, v| valid_key?(k) && valid_value?(v) }.values
```

Each of these code snippets must allocate an intermediate data structure. I propose allowing `Hash#keys` and `Hash#values` to accept optional block parameters that *take both key and value*. For example, the above code could be rewritten as:

```ruby
hash.keys { |_, v| v == :some_value }
hash.keys { |k, _| k.nil? || k.even? }
hash.values { |k, v| valid_key?(k) && valid_value?(v) }
```

This behavior:

1. Does not break any existing code (since `Hash#keys` and `Hash#values` do not currently accept blocks).
2. Is very readable—it's obvious what it does at a glance.
3. Is more efficient than current alternatives.
4. Is more concise than current alternatives.
5. Is flexible and useful in a variety of scenarios, because the block has access to both key and value (unlike the behavior proposed in #14788).



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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:97597] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output
  2020-03-24 21:00 [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output jacobevelyn
                   ` (2 preceding siblings ...)
  2020-03-25 15:31 ` [ruby-core:97596] " jacobevelyn
@ 2020-03-25 18:28 ` eregontp
  2020-04-04  5:39 ` [ruby-core:97713] " sawadatsuyoshi
  2020-07-10 16:09 ` [ruby-core:99112] " jacobevelyn
  5 siblings, 0 replies; 7+ messages in thread
From: eregontp @ 2020-03-25 18:28 UTC (permalink / raw
  To: ruby-core

Issue #16739 has been updated by Eregon (Benoit Daloze).


Thank you for the examples and links.

`filter_map` looks good enough for this to me.

In general I think we avoid adding blocks to core methods, because indeed it's not clear if people expect #map, #select or #filter_map behavior, and it's so much clearer with the explicit call.

----------------------------------------
Feature #16739: Allow Hash#keys and Hash#values to accept a block for filtering output
https://bugs.ruby-lang.org/issues/16739#change-84781

* Author: jacobevelyn (Jacob Evelyn)
* Status: Open
* Priority: Normal
----------------------------------------
I often see code like the following:

``` ruby
hash.select { |_, v| v == :some_value }.keys
hash.keys.select { |k| k.nil? || k.even? }
hash.select { |k, v| valid_key?(k) && valid_value?(v) }.values
```

Each of these code snippets must allocate an intermediate data structure. I propose allowing `Hash#keys` and `Hash#values` to accept optional block parameters that *take both key and value*. For example, the above code could be rewritten as:

```ruby
hash.keys { |_, v| v == :some_value }
hash.keys { |k, _| k.nil? || k.even? }
hash.values { |k, v| valid_key?(k) && valid_value?(v) }
```

This behavior:

1. Does not break any existing code (since `Hash#keys` and `Hash#values` do not currently accept blocks).
2. Is very readable—it's obvious what it does at a glance.
3. Is more efficient than current alternatives.
4. Is more concise than current alternatives.
5. Is flexible and useful in a variety of scenarios, because the block has access to both key and value (unlike the behavior proposed in #14788).



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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:97713] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output
  2020-03-24 21:00 [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output jacobevelyn
                   ` (3 preceding siblings ...)
  2020-03-25 18:28 ` [ruby-core:97597] " eregontp
@ 2020-04-04  5:39 ` sawadatsuyoshi
  2020-07-10 16:09 ` [ruby-core:99112] " jacobevelyn
  5 siblings, 0 replies; 7+ messages in thread
From: sawadatsuyoshi @ 2020-04-04  5:39 UTC (permalink / raw
  To: ruby-core

Issue #16739 has been updated by sawa (Tsuyoshi Sawada).


Includes a duplicate of #14788.

----------------------------------------
Feature #16739: Allow Hash#keys and Hash#values to accept a block for filtering output
https://bugs.ruby-lang.org/issues/16739#change-84918

* Author: jacobevelyn (Jacob Evelyn)
* Status: Open
* Priority: Normal
----------------------------------------
I often see code like the following:

``` ruby
hash.select { |_, v| v == :some_value }.keys
hash.keys.select { |k| k.nil? || k.even? }
hash.select { |k, v| valid_key?(k) && valid_value?(v) }.values
```

Each of these code snippets must allocate an intermediate data structure. I propose allowing `Hash#keys` and `Hash#values` to accept optional block parameters that *take both key and value*. For example, the above code could be rewritten as:

```ruby
hash.keys { |_, v| v == :some_value }
hash.keys { |k, _| k.nil? || k.even? }
hash.values { |k, v| valid_key?(k) && valid_value?(v) }
```

This behavior:

1. Does not break any existing code (since `Hash#keys` and `Hash#values` do not currently accept blocks).
2. Is very readable—it's obvious what it does at a glance.
3. Is more efficient than current alternatives.
4. Is more concise than current alternatives.
5. Is flexible and useful in a variety of scenarios, because the block has access to both key and value (unlike the behavior proposed in #14788).



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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:99112] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output
  2020-03-24 21:00 [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output jacobevelyn
                   ` (4 preceding siblings ...)
  2020-04-04  5:39 ` [ruby-core:97713] " sawadatsuyoshi
@ 2020-07-10 16:09 ` jacobevelyn
  5 siblings, 0 replies; 7+ messages in thread
From: jacobevelyn @ 2020-07-10 16:09 UTC (permalink / raw
  To: ruby-core

Issue #16739 has been updated by jacobevelyn (Jacob Evelyn).


sawa (Tsuyoshi Sawada) wrote in #note-5:
> Includes a duplicate of #14788.

I just want to note that this is a *more powerful* feature than what's proposed in #14788, because both the key and value would be available to the block.

Eregon (Benoit Daloze) wrote in #note-4:
> In general I think we avoid adding blocks to core methods, because indeed it's not clear if people expect #map, #select or #filter_map behavior, and it's so much clearer with the explicit call.

That's fair! Would calling these methods `filter_keys`/`filter_values` or `select_keys`/`select_values` be more explicit?

----------------------------------------
Feature #16739: Allow Hash#keys and Hash#values to accept a block for filtering output
https://bugs.ruby-lang.org/issues/16739#change-86487

* Author: jacobevelyn (Jacob Evelyn)
* Status: Open
* Priority: Normal
----------------------------------------
I often see code like the following:

``` ruby
hash.select { |_, v| v == :some_value }.keys
hash.keys.select { |k| k.nil? || k.even? }
hash.select { |k, v| valid_key?(k) && valid_value?(v) }.values
```

Each of these code snippets must allocate an intermediate data structure. I propose allowing `Hash#keys` and `Hash#values` to accept optional block parameters that *take both key and value*. For example, the above code could be rewritten as:

```ruby
hash.keys { |_, v| v == :some_value }
hash.keys { |k, _| k.nil? || k.even? }
hash.values { |k, v| valid_key?(k) && valid_value?(v) }
```

This behavior:

1. Does not break any existing code (since `Hash#keys` and `Hash#values` do not currently accept blocks).
2. Is very readable—it's obvious what it does at a glance.
3. Is more efficient than current alternatives.
4. Is more concise than current alternatives.
5. Is flexible and useful in a variety of scenarios, because the block has access to both key and value (unlike the behavior proposed in #14788).



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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-07-10 16:10 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-03-24 21:00 [ruby-core:97588] [Ruby master Feature#16739] Allow Hash#keys and Hash#values to accept a block for filtering output jacobevelyn
2020-03-25  3:31 ` [ruby-core:97590] " sawadatsuyoshi
2020-03-25 15:14 ` [ruby-core:97595] " mame
2020-03-25 15:31 ` [ruby-core:97596] " jacobevelyn
2020-03-25 18:28 ` [ruby-core:97597] " eregontp
2020-04-04  5:39 ` [ruby-core:97713] " sawadatsuyoshi
2020-07-10 16:09 ` [ruby-core:99112] " jacobevelyn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).