Get numbers from string - ruby

I got a string:
"1|2 3 4 oh 5 oh oh|e eewrewr|7|".
I want to get the digits between first pipes (|), returning "2 3 4 5".
Can anyone help me with the regular expression to do that?

Does this work?
"1|2 3 4 oh 5 oh oh|e eewrewr|7|".split('|')[1].scan(/\d/)

Arun's answer is perfect if you want only digits.
i.e.
"1|2 3 4 oh 5 oh oh|e eewrewr|7|".split('|')[1].scan(/\d/)
# Will return ["2", "3", "4", "5"]
"1|2 3 4 oh 55 oh oh|e eewrewr|7|".split('|')[1].scan(/\d/)
# Will return ["2", "3", "4", "5", "5"]
If you want numbers instead,
# Just adding a '+' in the regex:
"1|2 3 4 oh 55 oh oh|e eewrewr|7|".split('|')[1].scan(/\d+/)
# Will return ["2", "3", "4", "55"]

if you want to use just regex...
\|[\d\s\w]+\|
and then
\d
but that's probably not the best solution

Related

Is there ruby methods to select string between other strings?

I'm starting in programming and I'm looking to make a program for extracting all the words contained between two words within a text (in order store them in a variable )
For example with the words "START" & "STOP":
"START 1 2 3 STOP 5 6 START 7 8 STOP 9 10"
I would like to store in variables: 1 2 3 7 8
I started to do it with Ruby as you can see in the code below, my current idea was to convert the string "global" into an array and then number the position of string1 and string2; then create an array ‘string1’ with the values of the initial array # string1 + 1,… string2 -1.
Unfortunately, it works only once because the .index function only works on the first occurence...would there be a better way to do that ?
Thank you in advance for your help
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
start= text.split(' ')
a = start.index('start')
b = start.index('stop')
puts a
puts b
puts c = start[a+1,b-a-1].join(" ")
# returns
#1
#5
#2 3 4 ```
You could start with the scan-method and a regular expression:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
res1 = text.scan(/start\s*(.*?)\s*stop/) #[["2 3 4"], ["9 10"]]
res2 = res1.flatten #["2 3 4", "9 10"]
or without the intermediate variables:
res = text.scan(/start(.*?)stop/).flatten #["2 3 4", "9 10"]
Explanation:
See https://apidock.com/ruby/String/scan for the scan method.
The regular expression /start\s*(.*?)\s*stop/ is the combination of
start
\s*: any space character
(.*?):
The (and ) is responsible to remember the content.
. means any character, * means a repetition (zero or more characters), ? restrict the result to the shortest possibility (see below for details)
\s*: any space character
stop
The result is an array with hits of the regular expression. The regular expression could contain different parts to detect (multiple ()-pairs). So it is an array of arrays. In our case, each inner array has one element, so you can use flatten to get a 'flat' array.
If you would not use the ? in the regular expression, then you would find 2 3 4 stop 6 7 start 9 10 instead of the shorter parts.
You are not exactly getting an error, codereview might be a better place to ask. But since you are new in the community, here is a regular expression with lookaround assertions that does the job:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
text.scan(/start ((?:(?!start).)*?) stop/).join(' ')
# => "2 3 4 9 10"
Btw, a great place to test you regular expressions in Ruby is https://rubular.com/
I hope you find this helpful.
A One-Line Method Chain
Here's an approach based on String#scan:
text = "0 start 2 3 4 stop 6 7 start 9 10 stop 12"
text.scan(/\bstart\s+(.*?)\s+stop\b/i).flat_map { _1.flat_map &:split }
#=> ["2", "3", "4", "9", "10"]
The idea here is to:
Extract all string segments that are bracketed between case-insensitive start and stop keywords.
text.scan /\bstart\s+(.*?)\s+stop\b/i
#=> [["2 3 4"], ["9 10"]]
Extract words separated by whitespace from between your keywords.
[["2 3 4"], ["9 10"]].flat_map { _1.flat_map &:split }
#=> ["2", "3", "4", "9", "10"]
Caveats
Notable caveats to the approach outlined above include:
String#scan creates nested arrays, and the repeated calls to Enumerable#flat_map used to handle them are less elegant than I might prefer.
\b is a zero-width assertion, so looking for word boundaries can cause #scan to include leading and trailing whitespace in the results that then need to be handled by String#strip or String#split.
Substituting \s+ for \b handles some edge cases while creating others.
It doesn't do anything to guard against unbalanced pairs, e.g. "start 0 start 2 3 4 stop 6 stop".
For simple use cases, String#scan with a tuned regex is probably all you need. The more varied and unpredictable your input and data structures are, the more edge cases your parsing routines will need to handle.
Option using array: as a starting point I could suggest using Enumerable#slice_before after String#split
Given your command and the stop-words:
command = "START 1 2 3 STOP 5 6 START 7 8 STOP 9 10"
start = 'START'
stop = 'STOP'
You can use it something like that:
grouped_cmd = command.split.slice_before { |e| [start, stop].include? e } # .to_a
#=> [["START", "1", "2", "3"], ["STOP", "5", "6"], ["START", "7", "8"], ["STOP", "9", "10"]]
Then you can manipulate as you like, for example:
grouped_cmd.select { |first, *rest| first == start }
#=> [["START", "1", "2", "3"], ["START", "7", "8"]]
Or
grouped_cmd.each_with_object([]) { |(first, *rest), ary| ary << rest if first == start }
#=> [["1", "2", "3"], ["7", "8"]]
Or even
grouped_cmd.each_slice(2).map { |(start, *stt), (stop, *stp)| { start.downcase.to_sym => stt, stop.downcase.to_sym => stp } }
#=> [{:start=>["1", "2", "3"], :stop=>["5", "6"]}, {:start=>["7", "8"], :stop=>["9", "10"]}]
And so on.

Adding/Deleting numbers in an array via user input

I want to add/remove numbers in an array based on user input. Here's what I tried:
a = %w[1 2 3 4 5 6 7 8 9]
delete_list = []
puts a
puts "pick 1-9 to del"
input = gets.to_i
input << a
puts a
The last line is to check if it worked, and I get "no implicit conversion of Array into Integer". Is this because I used %w and the array isn't integer based?
a = %w[1 2 3 4 5 6 7 8 9]
a.map! {|e| e.to_i}
puts a
puts "pick 1-9 to del"
input = gets.chomp
a.delete(input)
puts a
Well, I changed it up like so. But I don't seem to be having success with the a.delete(input) command, as my array still prints out 1-9. What am I doing wrong?
To remove an element at specific position use Array#delete_at:
input = gets.to_i
a.delete_at(input - 1) # index starts from `0`
If you want to delete item not by position, but by value, use Array#delete.
input = gets.chomp # `a` contains strings; used `.chomp` instead of `.to_i`
a.delete(input)
Yes. It is because the argument to Fixnum#<< has to be an integer, not an array.
Focusing on the key lines of code:
a = %w[1 2 3 4 5 6 7 8 9]
This makes the variable "a" an array of string elements:
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
Then you set this variable:
input = gets.to_i
This gets a string from the user ("gets" - like an abbreviation of the name getStringFromUser) and then .to_i turns it to an integer.
This would have likely resulted in a "0" (if letters entered) or whatever integer was entered:
=>0 OR => #some integer
Then you tried to put an array into the integer:
input << a
Ruby tried to take the "a" array of elements (class Array) and cram it into that integer (aka: class Fixnum) variable "input". This is where you got your error - Ruby can't put an array into an integer using a method like "<<".
If you replaced the line:
input << a
With:
a << input
You'll at least get a functional result.
If the "gets" was say, input=9, then your last puts a would give you:
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", 9]
Which is an Array element that consists of a bunch of string elements and an integer element that was pushed to the end.
Now, from your puts "pick 1-9 to del", it seems like you want to delete an element from the array.
First, you'll want your array to be integers and not strings... something like:
a.map! {|e|e.to_i}
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
(if you hadn't converted the input to an integer, you could skip that last step... or oddly convert the "input" back to a string with input.to_s)
Now that "a" is an array of integers, you can delete one using the "delete" method for Arrays and telling it to delete the value of the "input" variable:
a.delete(input)
=> 9
#it returns the value you deleted.
Your last puts a would return:
=> [1, 2, 3, 4, 5, 6, 7, 8]
It's a long step-wise answer, but hopefully that helps.

Regex to catch groups of same digits in Ruby

Is it possible to catch all grous of same digits in string with regex on Ruby? I'm not familiar with regex.
I mean: regex on "1112234444" will produce ["111", "22", "3", "4444"]
I know, I can use (\d)(\1*), but it only gives me 2 groups in each match. ["1", "11"], ["2", "2"], ["3", -], ["4", "444"]
How can I get 1 group in each match? Thanks.
Here, give this a shot:
((\d)\2*)
You can use this regex
((\d)\2*)
group 1 catches your required value
My first quick answer was rightfully criticized for having no explanation for the code. So here's another one, better in all respects ;-)
We exploit the fact that the elements whose runs we want are digits and they are easy to enumerate by hand. So we construct a readable regex which means "a run of zeros, or a run of ones, ... or a run of nines". And we use the right method for the job, String#scan:
irb> "1112234444".scan(/0+|1+|2+|3+|4+|5+|6+|7+|8+|9+/)
=> ["111", "22", "3", "4444"]
For the record, here's my original answer:
irb> s = "1112234444"
=> "1112234444"
irb> rx = /(0+|1+|2+|3+|4+|5+|6+|7+|8+|9+)/
=> /(0+|1+|2+|3+|4+|5+|6+|7+|8+|9+)/
irb> s.split(rx).reject(&:empty?)
=> ["111", "22", "3", "4444"]

How to split to the right of a number?

I'm trying to use Ruby to split to the right of a number.
For example: H2SO4
How do you do this?
I'd like to output ["H2", "SO4"]
x.split(/\d+/) yields: ["H", "SO"]
x.split(//) yields: ["H", "2", "S", "O", "4"]
Both cool but not exactly what I'm looking for.
x.scan(/[A-za-z]*\d+/)
This means break it into groups, each of which contains 0 or more letters, then 1 or more digits. Or if the non-digits can be anything:
x.scan(/\D*\d+/)

How to split (chunk) a Ruby array into parts of X elements? [duplicate]

This question already has answers here:
How to chunk an array in Ruby
(2 answers)
Closed 5 years ago.
I have an array
foo = %w(1 2 3 4 5 6 7 8 9 10)
How can I split or "chunk" this into smaller arrays?
class Array
def chunk(size)
# return array of arrays
end
end
foo.chunk(3)
# => [[1,2,3],[4,5,6],[7,8,9],[10]]
Take a look at Enumerable#each_slice:
foo.each_slice(3).to_a
#=> [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"], ["10"]]
If you're using rails you can also use in_groups_of:
foo.in_groups_of(3)

Resources