Возвращает индекс всех вхождений символа в строке в ruby - PullRequest
20 голосов
/ 30 ноября 2009

Я пытаюсь вернуть индексы для всех вхождений определенного символа в строке, используя Ruby. Пример строки "a#asg#sdfg#d##" и ожидаемый результат [1,5,10,12,13] при поиске # символов. Следующий код выполняет работу, но должен быть более простой способ сделать это?

def occurances (line)

  index = 0
  all_index = []

  line.each_byte do |x|
    if x == '#'[0] then
      all_index << index
    end
    index += 1
  end

  all_index
end

Ответы [ 6 ]

20 голосов
/ 30 ноября 2009
s = "a#asg#sdfg#d##"
a = (0 ... s.length).find_all { |i| s[i,1] == '#' }
15 голосов
/ 30 ноября 2009
require 'enumerator' # Needed in 1.8.6 only
"1#3#a#".enum_for(:scan,/#/).map { Regexp.last_match.begin(0) }
#=> [1, 3, 5]

ETA: это работает, создав Enumerator, который использует scan(/#/) в качестве каждого метода.

scan выдает каждое вхождение указанного шаблона (в данном случае /#/), и внутри блока вы можете вызвать Regexp.last_match для доступа к объекту MatchData для совпадения.

MatchData#begin(0) возвращает индекс, с которого начинается совпадение, и, поскольку мы использовали map в перечислителе, мы возвращаем массив этих индексов.

13 голосов
/ 30 ноября 2009

Вот менее причудливый способ:

i = -1
all = []
while i = x.index('#',i+1)
  all << i
end
all

В быстром тесте скорости это было примерно в 3,3 раза быстрее, чем метод find_all FM, и примерно в 2,5 раза быстрее, чем метод enum_for в sepp2k.

2 голосов
/ 30 ноября 2009

Вот длинная цепочка методов:

"a#asg#sdfg#d##".
  each_char.
  each_with_index.
  inject([]) do |indices, (char, idx)|
    indices << idx if char == "#"
    indices
  end

# => [1, 5, 10, 12, 13]

требуется 1.8.7 +

1 голос
/ 01 декабря 2009

Другое решение, полученное из ответа FMC:

s = "a#asg#sdfg#d##"
q = []
s.length.times {|i| q << i if s[i,1] == '#'}

Мне нравится, что у Руби никогда не бывает только одного способа что-то сделать!

0 голосов
/ 24 ноября 2018

Вот решение для массивных струн. Я делаю поиск текста в текстовых строках размером 4,5 МБ, а другие решения останавливаются. Это использует тот факт, что ruby ​​.split очень эффективен по сравнению со сравнениями строк.

def indices_of_matches(str, target)
      cuts = (str + (target.hash.to_s.gsub(target,''))).split(target)[0..-2]
      indicies = []
      loc = 0
      cuts.each do |cut|
        loc = loc + cut.size
        indicies << loc
        loc = loc + target.size
      end
      return indicies
    end

Это в основном использование лошадиных сил за методом .split, затем использование отдельных частей и длины искомой строки для определения местоположения. Я перешел от 30 секунд, используя различные методы, к мгновенным на очень больших строках.

Я уверен, что есть лучший способ сделать это, но:

(str + (target.hash.to_s.gsub(target,'')))

добавляет что-то к концу строки в случае, если цель находится в конце (и способ разделения), но также необходимо убедиться, что «случайное» добавление не содержит самой цели.

indices_of_matches("a#asg#sdfg#d##","#")
=> [1, 5, 10, 12, 13]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...