Array.map { |x| change value } is removing it from the array, why? - ruby

The objective is to move each letter to the next letter in the alphabet,
within the map, it successfully changed the letter but once i'm out of there the value disappears, except the vowels. How come?
def LetterChanges(str)
abc = [*("a".."z")]
result = str.split(//)
result.map! do |x|
if abc.include?(x)
if x == "z"
x = "A"
else
x = abc[abc.index(x)+1]
# if you puts x here, you can see it changes value correctly
if x == "a" || x == "e" || x == "i" || x == "o" || x == "u"
x.capitalize!
end
end
end
#However here, the changed values that are not vowels disappear
# WHY is that happening, is the second if (vowel) affecting it? How?
end
puts "#{result.join}" #<--- its only putting the vowels
return result.join
end
LetterChanges("what the hell is going on?")

The block passed to map! needs to return a value in all cases for this to work.
http://www.ruby-doc.org/core-2.2.0/Array.html#method-i-map-21
def LetterChanges(str)
abc = [*("a".."z")]
result = str.split(//)
result.map! do |x|
if abc.include?(x)
if x == "z"
x = "A"
else
x = abc[abc.index(x)+1]
if x == "a" || x == "e" || x == "i" || x == "o" || x == "u"
x.capitalize!
end
end
end
x
end
result.join
end

The problem is your if. When x is a not a vowel that return nil.
Just Change this line
if x == "a" || x == "e" || x == "i" || x == "o" || x == "u"
x.capitalize!
end
With this
x = %w{a e i o u}.include?(x) ? x.capitalize : x

Related

Minesweeper game board | ruby

I have written a code for the drawing board for the minesweeper game, can anyone help me to refactor this code more.
Please find my code below
def draw(height, width, mines)
board = Array.new(height) { Array.new(width,0) }
x = Random.rand(height)
y = Random.rand(width)
mines.times do
until board[x][y] != 'x'
x = Random.rand(height)
y = Random.rand(width)
end
board[x][y] = 'x'
end
board.each_with_index do |row, i|
row.each_with_index do |elem, j|
next if board[i][j] == 'x'
count = 0
count += 1 if i+1 < height && board[i+1][j] == 'x'
count += 1 if j+1 < width && board[i][j+1] == 'x'
count += 1 if i-1 >= 0 && board[i-1][j] == 'x'
count += 1 if j-1 >= 0 && board[i][j-1] == 'x'
board[i][j] = count
end
end
board.each do |row|
row.each do |e|
print "#{e} "
end
print "\n"
end
end
draw(4,4,3)
Thanks in advance.
I think you need to check 8 adjacent cells, not 4. Since this is a refactoring, I kept the original behavior.
def draw(height, width, mines)
board = Array.new(height) { Array.new(width, 0) }
mines.times do
x = rand(height)
y = rand(width)
redo if board[x][y] == 'x'
board[x][y] = 'x'
[[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]].each do |x, y|
next if x < 0 || x >= height
next if y < 0 || y >= width
next if board[x][y] == 'x'
board[x][y] += 1
end
end
board.each { |row| puts row.join(' ') }
end

ruby access non-vowels of a string in condition line without arrays

I need to turn a string that contains (among other letters) a sequence of 3 letters consisting of a non-vowel, an "o" and the same non-vowel again
into
a string that contains (other letters and) only that non-vowel.
like
"kok" #=> "k"
"mom" #=> "m"
"lol" #=> "l"
"kokmomloljk" #=> "kmljk"
I would like my code to be as compact as possible and only use string methods.
str.each_char { | i | if i == /[^aeiou]/ and i == str[i.index + 2] and str[i.index + 1] == "o"
str = str.delete(str.slice(str[i.index + 1], 2))
end
}
The output is the unchanged string. Thank you in advance.
R = /
([^aeiou]) # match a consonant in capture group 1
o # match an 'o'
\1 # match the contents of capture group 1
/x # free-spacing regex definition mode
def my_method(str)
str.gsub(R,'\1')
end
my_method "my dog kok has fleas"
#=> "my dog k has fleas"
my_method "much momentum"
#=> "much mentum"
my_method "'plolly' is not a word"
#=> "'plly' is not a word"
my_method "abkokcmomloljkde"
#=> "abkcmljkde"
my_method "bub"
#=> "bub"
I was wondering if you could do this in a more functional non-destructieve way with map and, yes you can, but not more compact than the other answers:
str = "iskakmomlol"
VOWEL = /[aeiou]/
RESONANT = /[^aeiou]/
str.chars.map.with_index { |c, i|
prevb, prev, nxt, scnd = str[i - 2], str[i - 1], str[i + 1], str[i + 2]
if i > str.length - 1 || i == 0 then c
elsif c =~ RESONANT && nxt =~ VOWEL && c == scnd then c
elsif c =~ VOWEL && prev =~ RESONANT && nxt =~ RESONANT
elsif c =~ RESONANT && prevb == c && prev =~ VOWEL
else c
end
}.compact.join # "iskmljk"
Actually, this can be shorter:
R = /([^aeiou])[aeiou]\1/
str.chars.map.with_index { |c, i|
c unless str[i-1..i+1][R] || str[i-2..i][R]
}.compact.join # "iskmljk"
i figured since the "o" is a fix character in the sequence to be accepted, i can just go through with a count var and see if the characters next to it are equal to each other and non-vowels. also i found that slice can be also passed two parameters so that it slices starting at an offset and stopping after the given length.
index = 0
while index < str.length
index = index + 1
if str[index] == "o" and str[index-1] == str[index+1] and str[index-1] != /[^aeiou]/
str.slice!(index, 2)

Counting X's and O's in a string in Ruby

I'm not sure why my code is not working, I think my logic is right?
Have the function ExOh(str) take the str parameter being passed and return the string true if there is an equal number of x's and o's, otherwise return the string false. Only these two letters will be entered in the string, no punctuation or numbers. For example: if str is "xooxxxxooxo" then the output should return false because there are 6 x's and 5 o's.
ExOh(str)
i = 0
length = str.length
count_x = 0
count_o = 0
while i < length
if str[i] == "x"
count_x += 1
elsif str[i] == "o"
count_o += 1
end
i+=1
end
if (count_o == count_x)
true
elsif (count_o != count_x)
false
end
end
The problem with your code is the function declaration. Use def ExOh(str) at the start. It may help if you indented also.
def ExOh(str)
i = 0
length = str.length
count_x = 0
count_o = 0
while i < length
if str[i] == "x"
count_x += 1
elsif str[i] == "o"
count_o += 1
end
i+=1
end
if (count_o == count_x)
true
elsif (count_o != count_x)
false
end
end
By the way, a simpler solution using the standard library #count https://ruby-doc.org/core-2.2.0/String.html#method-i-count
def ExOh(str)
str.count('x') == str.count('o')
end

Using `each` to check whether all values are the same

I'm trying to evaluate whether every space value is equal to either "X" or "O". Can I use each to do that? Is there a better way?
if #spaces.each {|x| x=="O" || x=="X"}
#winner = true
puts "It's a tie!"
break
end
ETA: all? doesn't seem to be working, either. I got this error referring to the line with the block:
tictac.rb:47: syntax error, unexpected '|', expecting '}'
{|x| x=="O" || x=="X"}
^
tictac.rb:47: syntax error, unexpected '}', expecting keyword_end
Here is the entire TicTacToe I'm working on:
class Board
def initialize
#spaces = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.print_board
#winner = false
#turn = "X"
end
def print_board
puts
puts " " + #spaces[0].to_s + " " + #spaces[1].to_s + " " + #spaces[2].to_s
puts " " + #spaces[3].to_s + " " + #spaces[4].to_s + " " + #spaces[5].to_s
puts " " + #spaces[6].to_s + " " + #spaces[7].to_s + " " + #spaces[8].to_s
puts
end
def mark(turn, move)
space = #spaces.index(move)
#spaces[space] = turn
self.print_board
end
def play
while #winner == false
puts "where would you like to put your #{#turn}?"
move = gets.chomp.to_i
self.mark(#turn, move)
if
#spaces[0] == #turn && #spaces[1] == #turn && #spaces[2] == #turn ||
#spaces[3] == #turn && #spaces[4] == #turn && #spaces[5] == #turn ||
#spaces[6] == #turn && #spaces[7] == #turn && #spaces[8] == #turn ||
#spaces[0] == #turn && #spaces[3] == #turn && #spaces[6] == #turn ||
#spaces[1] == #turn && #spaces[4] == #turn && #spaces[7] == #turn ||
#spaces[2] == #turn && #spaces[5] == #turn && #spaces[8] == #turn ||
#spaces[0] == #turn && #spaces[4] == #turn && #spaces[8] == #turn ||
#spaces[2] == #turn && #spaces[4] == #turn && #spaces[6] == #turn
#winner = true
puts "#{#turn} is the winner!"
break
elsif #spaces.all?
{|x| x=="O" || x=="X"}
#winner = true
puts "It's a tie!"
break
else
#turn == "X"? #turn = "O" : #turn = "X"
end
end
end
end
game = Board.new
game.play
I marked an answer that worked, and I guess all? was better than my each, but I'm still curious why changing it to all? didn't seem to work.
#spaces.all? { |x| x=="O" || x=="X" }
You cannot put a newline between a method call and its block. That makes Ruby think you're calling the method without a block and defining a Hash.
All elements of #spaces will be "O" or "X" if
(#spaces - ["O", "X"]).empty?
is true.
#spaces = ["O", "X", "X", "O"]
(#spaces - ["O", "X"]).empty?
#=> true
#spaces = ["O", "X", "X", "O", "cat"]
(#spaces - ["O", "X"]).empty?
#=> false
Chain Array#Uniq and Array#Size
Refactor your check into a method which takes an array of box entries (e.g. "X" or "O"), or a variable holding such an array. The method will return true if all letters in the array are the same, because there will only be one unique character. For example:
def check_spaces letters
true if letters.uniq.size == 1
end
check_spaces %w[X X X] #=> true
check_spaces %w[O O O] #=> true
check_spaces %w[O X O] #=> nil
If you do this, you can even assign the result of #check_spaces directly to #winner, like so:
#winner = check_spaces %w[X X X]
#=> true
#winner = check_spaces %w[X O X]
#=> nil

Breaking an until loop once condition is met in Ruby

For example in the following code, I would want the loop to end as soon as the condition evaluates as true
x = "B"
until x == "A"
x = gets.chomp
puts "x is not equal to "A""
end
So if the user enters "F" they get the puts but if they enter "A" then the puts does not get outputted.
x = true assigns true to x so until x = true is equivalent to until true.
So, replace = with == in the following line:
until x = true
->
until x == true
Or, it will never end.
UPDATE
Use following code:
while true
x = gets.chomp
break if x == 'A'
puts 'x is not equal to "A"'
end
or
until (x = gets.chomp) == 'A'
puts 'x is not equal to "A"'
end
The keyword break will exit from a loop.
x = false
a = 0
b = 0
until x # is a boolean already so no need for == true
a = 1
b = 2
# code here that could change state of x
break if x # will break from loop if x == anything other than false or nil
a = 2
b = 1
end
Obviously, this is not good code but there is some useful concepts in there that you might be able to fish out.
EDIT
In response to your new code, it is proper use for until loop.
puts "x is not equal to 'A'" until (x = gets.chomp) == "A"

Resources