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/)

···

On Fri, 13 Jan 2012 00:15:24 +0900, Matt Lawrence wrote:

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.

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 12 January 2012 15:22, K Clair <kclair@pickledradish.com> wrote:

biglist !~ /bar/

!biglist.grep(/bar/)

On Fri, 13 Jan 2012 00:15:24 +0900, Matt Lawrence wrote:

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.

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 4:22 PM, K Clair <kclair@pickledradish.com> wrote:

biglist !~ /bar/

!biglist.grep(/bar/)

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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, 13 Jan 2012, Robert Klemme wrote:

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

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

Do you object to reject?

···

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

On Fri, 13 Jan 2012, Robert Klemme 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.

--
Chad Perrin [ original content licensed OWL: http://owl.apotheon.org ]

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 Thu, Jan 12, 2012 at 9:36 PM, Chad Perrin <code@apotheon.net> wrote:

On Fri, Jan 13, 2012 at 05:26:59AM +0900, Matt Lawrence wrote:
> On Fri, 13 Jan 2012, Robert Klemme 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?

--
Peter Vandenabeele
http://twitter.com/peter_v
http://rails.vandenabeele.com

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 Thu, Jan 12, 2012 at 10:22 PM, Peter Vandenabeele <peter@vandenabeele.com> wrote:

On Thu, Jan 12, 2012 at 9:36 PM, Chad Perrin <code@apotheon.net> wrote:

On Fri, Jan 13, 2012 at 05:26:59AM +0900, Matt Lawrence wrote:
> On Fri, 13 Jan 2012, Robert Klemme 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?

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:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

...

>
> 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:

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"]

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 Fri, Jan 13, 2012 at 3:18 PM, Robert Klemme <shortcutter@googlemail.com>wrote:

On Thu, Jan 12, 2012 at 10:22 PM, Peter Vandenabeele > <peter@vandenabeele.com> wrote:

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.

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 Fri, Jan 13, 2012 at 7:01 PM, Peter Vandenabeele <peter@vandenabeele.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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 6:06 AM, Robert Klemme <shortcutter@googlemail.com>wrote:

It's impossible because

- you cannot change behavior of !

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 1:51 PM, Josh Cheek <josh.cheek@gmail.com> wrote:

On Sat, Jan 14, 2012 at 6:06 AM, Robert Klemme > <shortcutter@googlemail.com>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/

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

>
>>
>> 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/

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.

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

···

On Sat, Jan 14, 2012 at 2:24 PM, Robert Klemme <shortcutter@googlemail.com>wrote:

On Sat, Jan 14, 2012 at 1:51 PM, Josh Cheek <josh.cheek@gmail.com> wrote:
> On Sat, Jan 14, 2012 at 6:06 AM, Robert Klemme > > <shortcutter@googlemail.com>wrote:

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 Rex

···

2012/1/14 Peter Vandenabeele <peter@vandenabeele.com>:

On Sat, Jan 14, 2012 at 2:24 PM, Robert Klemme > <shortcutter@googlemail.com>wrote:

On Sat, Jan 14, 2012 at 1:51 PM, Josh Cheek <josh.cheek@gmail.com> wrote:
> On Sat, Jan 14, 2012 at 6:06 AM, Robert Klemme >> > <shortcutter@googlemail.com>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/

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.

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