1

I'm using Ruby 2.4. I have an array with string data elements, that look like

["a word1 word2", "b eff", "a idaho orange", "new old shoe", "b mars", ...]

I want to form two arrays from the above, applying a function (.split(/^(a|b)[[:space]]*/i) to each element of the array. However, I can't figure out how to form two separate arrays. The following

arr.map{ |x| x.split(/^(a|b)[[:space]]*/i) }

only results in a single array and has a blank element in front of each element. Ideally, I'd like the output to be two arrays like

["a", "b", "a", "", "b", ...]
["word1 word2", "eff", "idaho orange", "new old shoe", "mars", ...]
2
  • Suppose "new old shoe" were "an old shoe" or "bold old shoe"? What would you expect the return value to be (considering that the regex would split on the first character of those two strings)? Did you mean [[:space:]]+ rather than [[:space:]]*? Commented Jan 22, 2017 at 20:13
  • When you give an example, 1) make all input values valid Ruby objects (no "...", among other things); 2) assign a variable to all input objects (e.g., arr = ["a word1 word2", "b eff", "a idaho orange", "new old shoe", "b mars"]) so that readers can cut and paste to test code and refer to those variables (arr) in answers in comments without having to define them; and 3) show your desired output as a valid Ruby object (no variables needed there). Commented Jan 22, 2017 at 20:18

2 Answers 2

2

Try this

arr.map { |x| a, b, c = x.partition(/^[ab]\s+/); [b.strip, a + c] }.transpose

How does this work?

  • partition splits a string into before-match, match and post-match
  • b.strip is the match without trailing whitespace
  • a + c is either the full string (if there was no match the full string is in before-match) or the post-match
  • [..., ...] creates a tuple, hence creating an array of tuple
  • transpose switches the rows and columns of a 2D array
Sign up to request clarification or add additional context in comments.

Comments

2

Regex only

This is the shortest I could find :

a1, a2 = arr.map{ |x| x.match(/((?:^[ab](?= ))?) *(.*)/).captures}.transpose

This example now works with "activity" or "ball". It checks if a space follows directly after a or b at the beginning of the string.

Split and logic

Another variant would be :

a1, a2 = arr.map do |x|
  parts = x.split(/(?<=^a|^b) +/)
  parts.unshift('') if parts.size == 1
  parts
end.transpose

2 Comments

Sweet! Maybe even use arr.map{ |x| x.match(/^([ab]?) ?(.*)/).captures}.transpose to better communicate intent.
Undeleting mine then :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.