Спасибо Марку Уилкинсу за вдохновение, но вот короткий код для его выполнения:
irb(main):015:0> s = "split on the word on okay?"
=> "split on the word on okay?"
irb(main):016:0> b=[]; s.split(/(on)/).each_slice(2) { |s| b << s.join }; b
=> ["split on", " the word on", " okay?"]
или:
s.split(/(on)/).each_slice(2).map(&:join)
См. Объяснение ниже сгиба.
Вот как это работает.Сначала мы разбиваем слово «on», но заключаем в скобки, чтобы превратить его в группу соответствияКогда в регулярном выражении есть группа совпадений, переданная в split
, Ruby включит эту группу в вывод:
s.split(/(on)/)
# => ["split", "on", "the word", "on", "okay?"
Теперь мы хотим объединить каждый экземпляр «on» с предыдущей строкой.each_slice(2)
помогает, передавая два элемента одновременно в свой блок.Давайте просто вызовем each_slice(2)
, чтобы увидеть, какие результаты.Поскольку each_slice
при вызове без блока возвращает перечислитель, мы применим to_a
к перечислителю, чтобы мы могли видеть, над чем перечислитель будет перечислять:
s.split(/(on)/).each_slice(2).to_a
# => [["split", "on"], ["the word", "on"], ["okay?"]]
Мы получаемблизко.Теперь все, что нам нужно сделать, это соединить слова вместе.И это приводит нас к полному решению выше.Я разверну его в отдельные строки, чтобы было легче следовать:
b = []
s.split(/(on)/).each_slice(2) do |s|
b << s.join
end
b
# => ["split on", "the word on" "okay?"]
Но есть отличный способ убрать временный b
и значительно сократить код:
s.split(/(on)/).each_slice(2).map do |a|
a.join
end
map
передает каждый элемент своего входного массива в блок;результат блока становится новым элементом в этой позиции в выходном массиве.В MRI> = 1.8.7 вы можете сократить его еще больше до эквивалента:
s.split(/(on)/).each_slice(2).map(&:join)