Разделите строку в список, но сохраните раздвоенный шаблон

В настоящее время я разделяю строку по шаблону, например:

outcome_array=the_text.split(pattern_to_split_by)

Проблема заключается в том, что сам шаблон, который я разделял, всегда опускается.

Как мне заставить его включить сам шаблон разделения?

Ответ 1

Спасибо Марку Уилкинсу за глупость, но здесь более короткий код для этого:

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 к Enumerator, чтобы мы могли видеть, что Enumerator перечислил:

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 передает каждый элемент своего входного массива в блок; результат блока становится новым элементом в этой позиции в выходном массиве. В МРТ >= 1,8,7 вы можете сократить его еще больше, до эквивалента:

s.split(/(on)/).each_slice(2).map(&:join)

Ответ 2

Вы можете использовать утверждение регулярного выражения для определения точки разделения без использования какого-либо входа. Ниже приведено положительное утверждение для разделения сразу после 'on':

s = "split on the word on okay?"
s.split(/(?<=on)/)
=> ["split on", " the word on", " okay?"]

Или положительный прогноз перед разбиением непосредственно перед 'on':

s = "split on the word on okay?"
s.split(/(?=on)/)
=> ["split ", "on the word ", "on okay?"]

С чем-то подобным, вы можете захотеть убедиться, что 'on' не является частью более крупного слова (например, "утверждение" ), а также удаляет пустое пространство в расколе:

"don't split on assertion".split(/(?<=\bon\b)\s*/)
=> ["don't split on", "assertion"]

Ответ 3

Если вы используете шаблон с группами, он также вернет шаблон в результаты:

irb(main):007:0> "split it here and here okay".split(/ (here) /)
=> ["split it", "here", "and", "here", "okay"]

Изменить. Дополнительная информация указала, что целью является включение элемента, на котором он был разделен, с одной из половин разделенных элементов. Я думаю, что есть простой способ сделать это, но я этого не знаю и сегодня не успел поиграть с ним. Таким образом, в отсутствие умного решения, это один из способов грубой силы. Используйте метод split, как описано выше, чтобы включить разделенные элементы в массиве. Затем выполните итерацию по массиву и объедините каждую вторую запись (которая по определению является значением разделения) с предыдущей записью.

s = "split on the word on and include on with previous"
a = s.split(/(on)/)

# iterate through and combine adjacent items together and store
# results in a second array
b = []
a.each_index{ |i|
   b << a[i] if i.even?
   b[b.length - 1] += a[i] if i.odd?
   }

print b

Результаты в этом:

["split on", " the word on", " and include on", " with previous"]