Project

General

Profile

Feature #9602

Logic with `Enumerable#grep`

Added by sawa (Tsuyoshi Sawada) over 3 years ago. Updated over 3 years ago.

Status:
Feedback
Priority:
Normal
Target version:
-
[ruby-core:61329]

Description

Enumerable#grep is useful to filter things:

[nil, {}, [], 1, :foo, "foo"].grep(String)
# => ["foo"]
  1. Often, the condition cannot be expressed as a single object on which === is applied, but as a disjunction over === applied to multiple objects. I would like Enumerable#grep to take arbitrary number of arguments, and when they are more than one, a logical disjunction applies, just as when there are multiple comma-separated objects after when in case condition:

    [nil, {}, [], 1, :foo, "foo"].grep(String, Symbol, Array)
    # => [[], :foo, "foo"]
    
  2. Also, it often happens that I want the negation of grep. Perhaps, Enumerable#grepv (grepv comes from grep -v) can be implemented as negation of Enumerable#grep, i.e., select elements for which === returns false on any of the arguments:

    [nil, {}, [], 1, :foo, "foo"].grepv(String, Symbol, Array)
    # => [nil, {}, 1]
    
select-to-accept-args.patch (2.62 KB) select-to-accept-args.patch srawlins (Sam Rawlins), 03/07/2014 04:18 AM

Related issues

Related to Ruby trunk - Feature #11049: Enumerable#grep_v (inversed grep)Closed

History

#1 [ruby-core:61332] Updated by matz (Yukihiro Matsumoto) over 3 years ago

  • Status changed from Open to Feedback
  • Assignee set to matz (Yukihiro Matsumoto)

I am afraid that proposed behavior is too far away from the original 'grep'.
We should use #select for more complex filtering.

Any opinion?

Matz.

#2 [ruby-core:61347] Updated by srawlins (Sam Rawlins) over 3 years ago

+1 to Matz's idea. I think this would work like Enumerable#count, taking *args or a block, but not both:

%w{foo bar baz}.select  #=> an Enumerator
%w{foo bar baz}.select {|e| e['b']}  #=> an Array ["bar", "baz"]
%w{foo bar baz}.select(/b/)  #=> an Array ["bar", "baz"]
%w{foo bar baz}.select(/f/, /z/)  #=> an Array ["foo", "baz"]
%w{foo bar baz}.select(/b/) {|e| e['f']}  # warns "given block not used", returns ["bar", "baz"]

I have a short patch for Enumerable#select on GitHub, and attached. This idea should be extended to Enumerable#reject, and Array's #select and #reject, and probably Array's #select! and #reject!, and maybe other classes that extend Enumerable, and override #select and #reject (like Struct#select).

If this patch is acceptable, I am happy to extend the patch for the other #select and #reject methods, and write tests.

#3 [ruby-core:61348] Updated by nobu (Nobuyoshi Nakada) over 3 years ago

Sam Rawlins wrote:

%w{foo bar baz}.select(/b/) {|e| e['f']}  # warns "given block not used", returns ["bar", "baz"]

It should raise a "wrong number of arguments" exception, IMO.

+    for (j=0; j<RARRAY_LEN(ary); j++) {
+ if (RTEST(rb_funcall(RARRAY_AREF(ary, j), id_eqq, 1, i))) {
+     rb_ary_push(memo->u2.value, i);

It should break after once matched.

#4 [ruby-core:61349] Updated by srawlins (Sam Rawlins) over 3 years ago

Hi Nobu, I was just using the same warning from enum_count, enum_find_index, and enum_inject. But it could raise if that is still desired. I'll fix with break.

#5 [ruby-core:61355] Updated by sawa (Tsuyoshi Sawada) over 3 years ago

Sam Rawlins wrote:

This idea should be extended to Enumerable#reject, and Array's #select and #reject, and probably Array's #select! and #reject!, and maybe other classes that extend Enumerable, and override #select and #reject (like Struct#select).

If this patch is acceptable, I am happy to extend the patch for the other #select and #reject methods, and write tests.

I agree on this feature. I think this what many people would want.

#6 [ruby-core:61404] Updated by srawlins (Sam Rawlins) over 3 years ago

I've updated my patch with Array#select, nobu's break suggestion, and documentation and tests:

If we want to go forward with this, I just need to do Enumerable#reject, Array#select!, Array#reject, Array#reject!, and I think Struct#select. I don't think this feature should be applied to Hash#select.

#7 [ruby-core:61804] Updated by srawlins (Sam Rawlins) over 3 years ago

I've updated my patch some more with Enumerable#reject and Array#reject. (Array#reject was weird... I reabsorbed ary_reject() into rb_ary_reject() and left the only use of rb_ary_push_1() intact. I think some of this code was very old.)

Updated code at GitHub: https://github.com/srawlins/ruby/compare/select-to-accept-args (as a patch)

All that remains is Array#select!, Array#reject!, and Struct#select.

#8 Updated by akr (Akira Tanaka) over 2 years ago

Also available in: Atom PDF