56

For example:

"Angry Birds 2.4.1".split(" ", 2)
 => ["Angry", "Birds 2.4.1"] 

How can I split the string into: ["Angry Birds", "2.4.1"]

ohho
  • 50,879
  • 75
  • 256
  • 383
  • 1
    the example is a bit unfortunate because we don't know if the breaking condition is the version number or that you simply want to split on the second ocurrence of a space. – tokland Aug 30 '12 at 08:11
  • 2
    split on the _last_ occurrence of a space – ohho Aug 30 '12 at 08:20

10 Answers10

121

String#rpartition, e.g.

irb(main):068:0> str = "Angry Birds 2.4.1"
=> "Angry Birds 2.4.1"
irb(main):069:0> str.rpartition(' ')
=> ["Angry Birds", " ", "2.4.1"]

Since the returned value is an array, using .first and .last would allow to treat the result as if it was split in two, e.g

irb(main):073:0> str.rpartition(' ').first
=> "Angry Birds"
irb(main):074:0> str.rpartition(' ').last
=> "2.4.1"
Vadym Tyemirov
  • 8,288
  • 4
  • 42
  • 38
  • 10
    Note that `String#rpartition` plays very nicely with Ruby's _"don't care" variable: [1] pry(main)> name, _, version = "Angry Birds 2.4.1".rpartition(' ') => ["Angry Birds", " ", "2.4.1"] [2] pry(main)> name => "Angry Birds" [3] pry(main)> version => "2.4.1" So no need for `Array#first` or `Array#last`... less is more! :-) – pvandenberk Apr 27 '15 at 17:26
11

I hava a solution like this:

class String
  def split_by_last(char=" ")
    pos = self.rindex(char)
    pos != nil ? [self[0...pos], self[pos+1..-1]] : [self]
  end
end

"Angry Birds 2.4.1".split_by_last  #=> ["Angry Birds", "2.4.1"]
"test".split_by_last               #=> ["test"]
halfelf
  • 9,737
  • 13
  • 54
  • 63
10

Something like this maybe ? Split where a space is followed by anything but a space till the end of the string.

"Angry Birds 2.4.1".split(/ (?=\S+$)/)
#=> ["Angry Birds", "2.4.1"]
oldergod
  • 15,033
  • 7
  • 62
  • 88
5

I don't seem able to get the example code in my comment properly formatted, so I'm submitting it as a separate answer, even though Vadym Tyemirov deserves all the credit for the String#rpartition solution he provided above.

I just wanted to add that String#rpartition plays very nicely with Ruby's "don't care" variable, as typically you're indeed only interested in the first and last element of the result array, but not the middle element (the separator):

[1] pry(main)> name, _, version = "Angry Birds 2.4.1".rpartition(' ')
=> ["Angry Birds", " ", "2.4.1"]
[2] pry(main)> name
=> "Angry Birds"
[3] pry(main)> version
=> "2.4.1"

So no need for Array#first or Array#last... less is more! :-)

Community
  • 1
  • 1
pvandenberk
  • 4,649
  • 2
  • 26
  • 14
2

"Angry Birds 2.4.1".split(/ (?=\d+)/)

sumskyi
  • 1,827
  • 13
  • 13
  • 1
    It solves this particular variation of problem, but it is not an answer to the question. – Anton Aug 30 '12 at 08:05
2

The rpartition solution makes a great sexy one-liner (I voted for it), but here's another technique if you want a one liner that's more flexible for solving more complex partitioning problems:

["Angry Birds 2.4.1".split(' ')[0..-2].join(' '), "Angry Birds 2.4.1".split(' ')[-1..-1].join(' ')]

By more flexible, I mean if there were more items being partitioned, you could just adjust the range of the sequence.

jsarma
  • 1,344
  • 11
  • 19
  • Could you explain why `-2` in `[0..-2]` – spuder Feb 05 '18 at 19:52
  • My answer before was incomplete @spuder. I just fixed it. Originally, I was assuming the question didn't care about the second half. [0..-2] means splice the last element off of the array, because in ruby -1 is the last element of the array. – jsarma Feb 08 '18 at 14:43
2

Create a String#split_on_last method.

Heavily inspired by halfelf's answer but permits more than just a single character, doesn't have a default param value and refactored for clarity.

Definition

class String
  def split_on_last( text )
    position_of_last_occurrence = self.rindex( text )

    return [ self ] if position_of_last_occurrence.nil?

    first_part = self[ 0...position_of_last_occurrence ]
    last_part  = self[ position_of_last_occurrence + text.length..-1 ]

    [ first_part, last_part ]
  end
end

Usage

"Angry Birds 2.4.1".split_on_last( " " )
#=> ["Angry Birds", "2.4.1"]

"start middle end end suffix".split_on_last( "end" )
#=> ["start middle end ", " suffix"]

"start middle suffix".split_on_last( "end" ) # No occurrence.
#=> ["start middle suffix"]
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
1

This is probably way too tricky (and probably not particularly efficient), but you can do this:

"Angry Birds 2.4.1".reverse.split(" ", 2).map(&:reverse).reverse
matthew.tuck
  • 1,267
  • 9
  • 11
1

reverse, split, then reverse every element and elements in array

"Angry Birds 2.4.1".reverse.split(' ', 2).map(&:reverse).reverse
Vitali
  • 2,576
  • 1
  • 11
  • 3
0
class String
  def divide_into_two_from_end(separator = ' ')
    self.split(separator)[-1].split().unshift(self.split(separator)[0..-2].join(separator))
  end
end

"Angry Birds 2.4.1".divide_into_two_from_end(' ') #=> ["Angry Birds", "2.4.1"]
Jing Li
  • 14,547
  • 7
  • 57
  • 69