Negative grep

A bit of syntax that I have never picked up. How do I use grep to
exclude
entries in a list?

biglist.grep(/foo/) # get all entries containing foo
biglist.grep(???) # get all entries that do not contain bar

– Matt
It’s not what I know that counts.
It’s what I can remember in time to use.

biglist !~ /bar/

!biglist.grep(/bar/)

Or there’s a complicated Regex feature called negative lookarounds, bit
more info here:

Which given:
a = [“abc”, “def”, “ghi”]

a.grep(/^((?!abc).)*$/)

=> [“def”, “ghi”]

On Fri, 13 Jan 2012, Robert K. wrote:

I’d use #select with inverted condition or #reject

irb(main):005:0> a.select {|s| /0/ !~ s}

That worked perfectly, thank you. A a sysadmin, I use “grep -v” quite a
bit and I was hoping that the grep method in Ruby had a similar
capability. Using select is a bit wordier and not quite as apparent as
to
what is going on, but it works very well.

– Matt
It’s not what I know that counts.
It’s what I can remember in time to use.

On Fri, Jan 13, 2012 at 05:26:59AM +0900, Matt L. wrote:

On Fri, 13 Jan 2012, Robert K. wrote:

I’d use #select with inverted condition or #reject

irb(main):005:0> a.select {|s| /0/ !~ s}

That worked perfectly, thank you. A a sysadmin, I use “grep -v”
quite a bit and I was hoping that the grep method in Ruby had a
similar capability. Using select is a bit wordier and not quite as
apparent as to what is going on, but it works very well.

Do you object to reject?

On Thu, Jan 12, 2012 at 4:22 PM, K Clair [email protected]
wrote:

biglist !~ /bar/

!biglist.grep(/bar/)

This was a joke, wasn’t it?

irb(main):001:0> a=19.times.map &:to_s
=> [“0”, “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “10”, “11”,
“12”, “13”, “14”, “15”, “16”, “17”, “18”]
irb(main):002:0> a !~ /0/
=> true
irb(main):003:0> !a.grep /0/
=> false

I’d use #select with inverted condition or #reject

irb(main):005:0> a.select {|s| /0/ !~ s}
=> [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “11”, “12”, “13”,
“14”, “15”, “16”, “17”, “18”]
irb(main):006:0> a.reject {|s| /0/ =~ s}
=> [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “11”, “12”, “13”,
“14”, “15”, “16”, “17”, “18”]

Cheers

robert

On Thu, Jan 12, 2012 at 9:36 PM, Chad P. [email protected] wrote:

apparent as to what is going on, but it works very well.

Do you object to reject?

Well, a ‘v’ option, equivalent to the UNIX grep seems quite
straightforward
to conceive?

peterv@ASUS:~$ ruby -v
ruby 1.9.3p0 (2011-10-30 revision 33570) [i686-linux]
peterv@ASUS:~$ echo -e “abc\ndef\nghi” | grep abc
abc
peterv@ASUS:~$ echo -e “abc\ndef\nghi” | grep -v abc
def
ghi
peterv@ASUS:~$ ruby -e ‘puts %w{ abc def ghi}.grep(/abc/)’
abc
peterv@ASUS:~$ ruby -e ‘puts %w{ abc def ghi}.grep(/abc/v)’
-e:1: unknown regexp option - v

Having “def” and “ghi” as a result here would be nice :slight_smile:

Peter

On Fri, Jan 13, 2012 at 3:18 PM, Robert K.
[email protected]wrote:

On Thu, Jan 12, 2012 at 10:22 PM, Peter V.
[email protected] wrote:

def
module Enumerable
=> [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”]

Thank you.

It took me quite some time to grasp that the ‘-i’ and ‘-v’ options of
$ grep -i
$ grep -v
do something very different …

‘-i’ changes the behavior of the Regexp itself

‘-v’ changes the behavior of the filter on the lines, after the regexp
is
calculated
(changes it from “ruby select” to “ruby reject”)

So, I believe now that my earlier proposal of a Ruby regexp like /…/v
does
not make a lot of sense in this context.

Negation could be an option for the Enumerable grep command, but that
is probably more complex than just implementing the grep_v you
suggested.

Thanks,

Peter

On Thu, Jan 12, 2012 at 10:22 PM, Peter V.
[email protected] wrote:

quite a bit and I was hoping that the grep method in Ruby had a
ruby 1.9.3p0 (2011-10-30 revision 33570) [i686-linux]
Having “def” and “ghi” as a result here would be nice :slight_smile:
Pretty easy to do:

module Enumerable
def grep_v(cond)
select {|x| not cond === x}
end
end

irb(main):006:0> a=10.times.map &:to_s
=> [“0”, “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”]
irb(main):007:0> a.grep /0/
=> [“0”]
irb(main):008:0> a.grep_v /0/
=> [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”]

Kind regards

robert

On Fri, Jan 13, 2012 at 7:01 PM, Peter V.
[email protected] wrote:

So, I believe now that my earlier proposal of a Ruby regexp like /…/v does
not make a lot of sense in this context.

I think it could be done but there would be some odd effects:

  • Match ($&) would always be the whole string (regexp doesn’t match)
    or nil (regexp matches).
  • capturing groups would never be set
  • a lot of generic code which relies on regexps to positively match
    could be broken if handed a /v regexp

All in all I’d say it’s probably not such a good idea. :slight_smile:

Negation could be an option for the Enumerable grep command, but that
is probably more complex than just implementing the grep_v you suggested.

It’s impossible because

  • you cannot change behavior of !
  • even if you could, at the moment when ! (or any other negation)
    should take place the information needed to calculate the result (i.e.
    the entries filtered out) is no longer accessible (it has been
    filtered out).

Kind regards

robert

On Sat, Jan 14, 2012 at 6:06 AM, Robert K.
[email protected]wrote:

It’s impossible because

  • you cannot change behavior of !

You can do this (I’m not advocating it), but IDK how you would actually
negate it.

class Regexp
def !@
/some sort of negation of #{source}/
end
end

abc = /abc/
!abc # => /some sort of negation of abc/

On Sat, Jan 14, 2012 at 1:51 PM, Josh C. [email protected]
wrote:

negate it.

class Regexp
def !@
/some sort of negation of #{source}/
end
end

abc = /abc/
!abc # => /some sort of negation of abc/

Oh, I wasn’t aware of this. Thank you for the education!

The way to use it would probably be to create a wrapper instance which
reverses semantics of the original’s Regexp instance and sets global
variables accordingly (if this is possible; not sure in the case of
$1, $2 etc. because of their special nature).

Still the other obstacle would prevent making !enum.grep(/foo/) return
what is intended. You could only do enum.grep(!/foo/). There is a
potential though to break other code which relies on different boolean
behavior of Regexp instances so the approach with #grep_v or an
explicit Regexp#negate would be better.

Kind regards

robert

On Sat, Jan 14, 2012 at 2:24 PM, Robert K.
[email protected]wrote:

You can do this (I’m not advocating it), but IDK how you would actually

behavior of Regexp instances so the approach with #grep_v or an
explicit Regexp#negate would be better.

I meant my earlier proposal

Negation could be an option for the Enumerable grep command

more along these lines:

describe “grep_with_options” do

it “works as before with 1 arugment” do
%w{abc def ghi}.grep(/abc/).should == %w{abc}
end

it “negates the selection with ‘v’” do
%w{abc def ghi}.grep(/abc/, ‘v’).should == %w{def ghi}
end

it “can be chained” do
%w{abc adf agh bcd efd}.grep(/^a/).grep(/c/,‘v’).should == %w{adf
agh}
end

it “can select lines after with A” do
pending(“TODO”)
%w{abc def ghi}.grep(/abc/, ‘A1’).should == %w{abc def}
end

end

a naive implementation

module Enumerable
def grep(cond, *select_options)
negate_matches = select_options.include?(‘v’)
if negate_matches
reject {|e| cond === e}
else
select {|e| cond === e}
end
end
end

Just the ‘v’ option to grep would already be useful
(and it would match with the request from the OP).

There are other grep options that I use quite often too
and could be implemented (-A -B -C -c …) similarly.

Peter

I don’t know if you guys realize there’s a discussion about this going
at Ruby’s Redmine, Feature #5588: add negation flag (v) to Regexp - Ruby master - Ruby Issue Tracking System, and somebody
even whipped up some patches.

Changing the grep method instead of the regex seems like a much more
reasonable idea.

– Matma R.

2012/1/14 Peter V. [email protected]: