Рубиновый эквивалент grep -v - PullRequest
8 голосов
/ 12 ноября 2010

Это то, что я делал вместо этого:

my_array.reject { |elem| elem =~ /regex/ }.each { ... }

Я чувствую, что это немного громоздко, но я не нашел ничего встроенного, что позволило бы мне изменить его на my_array.grepv /regex/ { ... }

Есть ли такая функция?

Ответы [ 10 ]

11 голосов
/ 24 ноября 2015

В Ruby 2.3 реализован метод Enumerable#grep_v, который вам нужен.

https://ruby -doc.org / core-2.3.0 / Enumerable.html # method-i-grep_v

7 голосов
/ 18 ноября 2010

Вы знаете, как Symbol#to_proc помогает с цепочкой? Вы можете сделать то же самое с регулярными выражениями:

class Regexp
  def to_proc
    Proc.new {|string| string =~ self}
  end
end

["Ruby", "perl", "Perl", "PERL"].reject(&/perl/i)
=> ["Ruby"]

Но вы, вероятно, не должны. Grep не просто работает с регулярными выражениями - вы можете использовать его следующим образом

[1,2, "three", 4].grep(Fixnum)

и, если вы хотите использовать grep -v, вам нужно реализовать Class#to_proc, что звучит неправильно.

4 голосов
/ 02 июля 2014

А как насчет инвертирования регулярного выражения?

["ab", "ac", "bd"].grep(/^[^a]/) # => ["bd"]
4 голосов
/ 12 ноября 2010

Как насчет этого?

arr = ["abc", "def", "aaa", "def"]
arr - arr.grep(/a/)  #=> ["def", "def"]

Я специально включил копию, чтобы убедиться, что все они возвращены.

3 голосов
/ 12 ноября 2010

Я не верю, что есть что-то подобное, но достаточно просто добавить:

class Array
  def grepv(regex, &block)
    self.reject { |elem| elem =~ regex }.each(&block)
  end
end

Обратите внимание, что при вызове этой функции вам нужно использовать символы равенства в регулярном выражении, в противном случае вы получите синтаксическую ошибку:

myarray.grepv(/regex/) { ... }
1 голос
/ 12 ноября 2010

Вы можете сделать:

my_array.reject{|e| e[/regex/]}.each { ... }

, но на самом деле сложно быть более кратким и самодокументирующим.Его можно написать, используя grep(/.../) с некоторым шаблоном негативного взгляда, но тогда я думаю, что становится сложнее понять общее действие, потому что сам шаблон сложнее понять.

0 голосов
/ 19 февраля 2015

Вот еще один выстрел, с разбрызгиванием ответов bltxd и Hsiu , и мы надеемся сохранить как можно больше духа оригинального grep(даже если это многословно):

module Enumerable
  def grepv(condition)
    if block_given?
      each do |item|
        yield item if not condition === item
      end
    else
      inject([]) do |memo, item|
        memo << item if not condition === item
        memo
      end
    end
  end
end

Если вы поставите блок, то все будет ленивым, как и следовало ожидать.Если вы не предоставите блок, есть небольшой дублирующий код.Мне бы очень хотелось, чтобы ответ Эндрю Гримма был применен в общем случае.

>> (%w(1 2 3) + [4]).cycle(3).grepv(Fixnum)
=> ["1", "2", "3", "1", "2", "3", "1", "2", "3"]

>> (%w(1 2 3) + [4]).cycle(3).grepv(/[12]/)
=> ["3", 4, "3", 4, "3", 4]

Ни в одном случае вы не платите до O(n^2) за сравнение предметов, как вхудший случай, если вы делаете вычитание массива.

0 голосов
/ 17 ноября 2010

Спасибо всем за ваши комментарии. В итоге я сделал это так:

module Enumerable
    def grepv(condition)

        non_matches = []

        self.each do |item|
            unless condition === item or condition === item.to_s
                non_matches.push(item)
                yield item if block_given?
            end
        end

        return non_matches
    end
end

Не уверен, что это лучший способ, потому что я только начинаю работать с Ruby. Это немного дольше, чем решения других людей, но мне это нравится, потому что он довольно аналогичен опции Enumerable grep - он работает со всем, что может обрабатывать ===, как grep, и возвращает элементы, которые он находит, если блок дано, и в любом случае возвращает массив из тех, которые не совпадают.

Я добавил часть or to_s, чтобы любые целые числа, например, перемежающиеся в массиве, могли совпадать с одним и тем же регулярным выражением, хотя я мог предположить, что это иногда может испортить вещи.

0 голосов
/ 12 ноября 2010

Вам просто нужно отменить результат соответствия регулярному выражению.

Enumerable.module_eval do
  def grepv regexp
    if block_given?
      self.each do |item|
        yield item if item !~ regexp
      end
    else
      self.find_all do |item|
        item !~ regexp
      end
    end
  end
end
0 голосов
/ 12 ноября 2010

Попробуйте использовать Array#collect!

my_array.collect! do |elem|
  if elem =~ /regex/
    # do stuff
    elem
  end
end

РЕДАКТИРОВАТЬ: Извините, тогда вам придется позвонить Array#compact после. По крайней мере, это устранит второй блок. Но это больше физический код. Это зависит от того, сколько «вещей» вы делаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...