Ruby: increment all integers in a string by +1 - ruby

I am looking for a succinct way to increment all the integers found in a string by +1 and return the full string.
For example:
"1 plus 2 and 10 and 100"
needs to become
"2 plus 3 and 11 and 101"
I can find all the integers very easily with
"1 plus 2 and 10 and 100".scan(/\d+/)
but I'm stuck at this point trying to increment and put the parts back together.
Thanks in advance.

You could use the block form of String#gsub:
str = "1 plus 2 and 10 and 100".gsub(/\d+/) do |match|
match.to_i + 1
end
puts str
Output:
2 plus 3 and 11 and 101

The gsub method can take in a block, so you can do this
>> "1 plus 2 and 10 and 100".gsub(/\d+/){|x|x.to_i+1}
=> "2 plus 3 and 11 and 101"

The thing with your regex is that it doesn't preserve your original string in the chain in order to put it back. What I did was to split it using spaces, detect which are words or integers using w.to_i != 0 (not counting 0 as an integer, you might want to improve this), add one, and join it back:
s = "1 plus 2 and 10 and 100"
s.split(" ").map{ |e| if (e.to_i != 0) then e.to_i+1 else e end }.join(" ")
=> "2 plus 3 and 11 and 101"

Related

Returning the highest and lowest numbers in a string: Ruby

Not sure what I'm doing incorrect but I seem to be getting it woefully wrong.
The question is, you are given a string of space separated numbers, and have to return the highest and lowest number.
Note:
All numbers are valid Int32, no need to validate them.
There will always be at least one number in the input string.
Output string must be two numbers separated by a single space, and highest number is first.
def high_and_low(numbers)
# numbers contains a string of space seperated numbers
#return the highest and lowest number
numbers.minmax { |a, b| a.length <=> b.length }
end
Output:
`high_and_low': undefined method `minmax' for "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6":String
minmax is not implemented for a string. You need to split your string into an array first. But note that split will return an array of strings, not numbers, you will need to translate the strings to integers (to_i) in the next step.
Because minmax returns the values in the opposite order than required, you need to rotate the array with reverse and then just join those numbers with whitespace for the final result.
numbers = "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6"
def high_and_low(numbers)
numbers.split.minmax_by(&:to_i).reverse.join(' ')
end
high_and_low(numbers)
#=> "542 -214"
How about:
numbers_array = numbers.split(' ')
"#{numbers_array.max} #{numbers_array.min}"
If you're starting with a string of numbers you may have to cast the .to_i after the call to split.
In that case:
numbers_array = numbers.split(' ').map { |n| n.to_i }
"#{numbers_array.max} #{numbers_array.min}"
As you're starting with a String, you must turn it into an Array to cast minmax on it.
Also, make sure to compare Integers by casting .map(&:to_i) on the Array; otherwise you'd compare the code-point instead of the numerical value.
def get_maxmin(string)
string.split(' ')
.map(&:to_i)
.minmax
.reverse
.join(' ')
end
There is no need to convert the string to an array.
def high_and_low(str)
str.gsub(/-?\d+/).
reduce([-Float::INFINITY, Float::INFINITY]) do |(mx,mn),s|
n = s.to_i
[[mx,n].max, [mn,n].min]
end
end
high_and_low "4 5 29 54 4 0 -214 542 -64 1 -3 6 -6"
#=> [542, -214]
Demo
This uses the form of String#gsub that has one argument and no block, so it returns an enumerator that I've chained to Enumerable#reduce (a.k.a. inject). gsub therefore merely generates matches of the regular expression /-?\d+/ and performs no substitutions.
My solution to this kata
def high_and_low(numbers)
numbers.split.map(&:to_i).minmax.reverse.join(' ')
end
Test.assert_equals(high_and_low("4 5 29 54 4 0 -214 542 -64 1 -3 6 -6"), "542 -214")
#Test Passed: Value == "542 -214"
Some docs about methods:
String#split Array#map Array#minmax Array#reverse Array#join
More about Symbol#to_proc
numbers.split.map(&:to_i) is same as number.split.map { |p| p.to_i }
But "minmax_by(&:to_i)" looks better, for sure I guess.

Iterator does not work in a simple loop

Very annoying mistake, but I'm still learning, so please be helpful.
I read a file, make a table and iterate it. Was wondering why my iteration incrementation does not work in this type of loop.
nrOfWordsInOneLine_array = Array.new { Hash.new }
iterator = 0
nrOfWordsInOneLine_array.each_with_index do |i, j|
iterator =+ 1
puts "Word in line #{j+1} #{iterator} is: #{i.length} and the longest one is #{i.max_by(&:length)} with #{i.max_by(&:length).length} letters"
end
output:
Word in line 1 1 is: 8 and the longest one is First with 5 letters
Word in line 2 1 is: 6 and the longest one is Second with 6 letters
Word in line 3 1 is: 4 and the longest one is Fourth with 6 letters
Word in line 4 1 is: 2 and the longest one is Fifth with 5 letters
iterator =+ 1 You have the + and = around the wrong way.
You want iterator += 1, right now you are setting iterator to +1 which is 1.

Inserting a space in between characters using gsub - Ruby

Let's say I had a string "I have 36 dogs in 54 of my houses in 24 countries".
Is it possible by using only gsub to add a " " between each digit so that the string becomes "I have 3 6 dogs in 5 4 of my houses in 2 4 countries"?
gsub(/(\d)(\d)/, "#{$1} #{$2}") does not work as it replaces each digit with a space and neither does gsub(/\d\d/, "\d \d"), which replaces the each digit with d.
s = "I have 3651 dogs in 24 countries"
Four ways to use String#gsub:
Use a positive lookahead and capture group
r = /
(\d) # match a digit in capture group 1
(?=\d) # match a digit in a positive lookahead
/x # extended mode
s.gsub(r, '\1 ')
#=> "I have 3 6 5 1 dogs in 2 4 countries"
A positive lookbehind could be used as well:
s.gsub(/(?<=\d)(\d)/, ' \1')
Use a block
s.gsub(/\d+/) { |s| s.chars.join(' ') }
#=> "I have 3 6 5 1 dogs in 2 4 countries"
Use a positive lookahead and a block
s.gsub(/\d(?=\d)/) { |s| s + ' ' }
#=> "I have 3 6 5 1 dogs in 2 4 countries"
Use a hash
h = '0'.upto('9').each_with_object({}) { |s,h| h[s] = s + ' ' }
#=> {"0"=>"0 ", "1"=>"1 ", "2"=>"2 ", "3"=>"3 ", "4"=>"4 ",
# "5"=>"5 ", "6"=>"6 ", "7"=>"7 ", "8"=>"8 ", "9"=>"9 "}
s.gsub(/\d(?=\d)/, h)
#=> "I have 3 6 5 1 dogs in 2 4 countries"
An alternative way is to look for the place between the numbers using lookahead and lookbehind and then just replace that with a space.
[1] pry(main)> s = "I have 36 dogs in 54 of my houses in 24 countries"
=> "I have 36 dogs in 54 of my houses in 24 countries"
[2] pry(main)> s.gsub(/(?<=\d)(?=\d)/, ' ')
=> "I have 3 6 dogs in 5 4 of my houses in 2 4 countries"
In order to reference a match you should use \n where n is the match, not $1.
s = "I have 36 dogs in 54 of my houses in 24 countries"
s.gsub(/(\d)(\d)/, '\1 \2')
# => "I have 3 6 dogs in 5 4 of my houses in 2 4 countries"

Print numbers in a range

I am trying to print all numbers between 1 and 50, using the following code:
[1..50].each{|n| puts n}
but the console print
[1..50]
I want to print something like this
1
2
3
4
...
50
Try the following code:
(1..50).each { |n| puts n }
The problem is that you're using [] delimiter instead of () one.
You can use [1..10] with a minor tweak:
[*1..10].each{ |i| p i }
outputs:
1
2
3
4
5
6
7
8
9
10
The * (AKA "splat") "explodes" the range into its components, which are then used to populate the array. It's similar to writing (1..10).to_a.
You can also do:
puts [*1..10]
to print the same thing.
So, try:
[*1..10].join(' ') # => "1 2 3 4 5 6 7 8 9 10"
or:
[*1..10] * ' ' # => "1 2 3 4 5 6 7 8 9 10"
To get the output you want.
The error here is that you are creating an Array object with a range as its only element.
> [1..10].size
=> 1
If you want to call methods like each on a range, you have to wrap the range in parentheses to avoid the method being called on the range's last element rather than on the range itself.
=> (1..10).each { |i| print i }
12345678910
Other ways to achieve the same:
(1..50).each { |n| print n }
1.up_to(50) { |n| print n }
50.times { |n| print n }
You can cast your range (in parentheses) to an array ([1 2 3 4 5 6... 48 49 50]) and join each item (e.g. with ' ' if you want all items in one line).
puts (1..50).to_a.join(' ')
# => 1 2 3 4 5 6 7 ... 48 49 50

Start a loop from 1

I recently came upon the scary idea that Integer.count loops in Ruby start from 0 and go to n-1 while playing with the Facebook Engineering puzzlers. I did the dirty fix of adding one to the block variable in the beginning so that it would start at one instead.
Is there a prettier way?
Example:
10.times do |n|
n += 1
puts n
end #=> 012345789
Ruby supports a number of ways of counting and looping:
1.upto(10) do |i|
puts i
end
>> 1.upto(10) do |i|
> puts i
| end #=> 1
1
2
3
4
5
6
7
8
9
10
There's also step instead of upto which allows you to increment by a step value:
>> 1.step(10,2) { |i| puts i } #=> 1
1
3
5
7
9
You could use a range:
(1..10).each { |i| puts i }
Ranges give you full control over the starting and ending indexes (as long as you want to go from a lower value to a higher value).
Try
(1..10).each do |i|
# ... i goes from 1 to 10
end
instead. It is also easier to read when the value of i matters.
Old, but this might be something somebody's lookin for..
5.times.with_index(100){|i, idx| p i, idx};nil
#=>
0
100
1
101
2
102
3
103
4
104
There is of course the while-loop:
i = 1
while i<=10 do
print "#{i} "
i += 1
end
# Outputs: 1 2 3 4 5 6 7 8 9 10

Resources