I am trying to .insert a space before the Uppercase letter if it's found.
Here's what I came up with, but it seems like it's an infinite loop. I don't know why:
def solution(string)
str = string.split("")
str.each_with_index do |l, i|
if l.upcase
str.insert(l[i], " ")
end
end
str.join("")
end
please let me know what I'm missing.
Because it's often a bad idea changing the object you're looping on. You insert a space before the upcase letter you found, so the next iteration you found the upcase letter again and everything repeats.
In this case regular expression seems to fit nicely
def solution(string)
string.gsub(/[[:upper:]]/, ' \0')
end
Related
Maybe it's not common to do like this, but anyway. I'm curious why it's not working.
I encountered with a strange behavior which I don't fully understand.
string = "sdsdasda asdas asdas"
words = string.split
words.map! do |word|
word.split(//).map! do |character|
character.upcase #or any other operations that change character
end.join
end
p words.join(" ")
If I instead of odd "end.join" construction this
end
word.join
end
I get an error, because |word| remains the same (string and not upcase), despite of using
.map!
with an exclamation point.
So basically my question is why map! won't change the word.
.map! is being called on the result of .split(//). It's modifying the resulting array, not the original word
You can think of the result of .split(//) being placed in a new variable:
characters = word.split(//)
characters.map! do |character|
character.upcase
end
return characters.join
# `word` is not modified
The last line of code in your block determines the value returned to the previous loop.
By adding .join to the end you are essentially saying that you want to return the result of that block of code to the previous map! call. But when you add an additional line afterwards to explicitly do word.jointhen you are saying you want to return the result of word.join to the previous map! call. As Leny Sirivong pointed out, that word is still lowercase (you can confirm by printing out word right there).
Consider this alternate syntax to make it clearer:
string = "sdsdasda asdas asdas"
words = string.split
words.map! do |word|
result = word.split(//).map! {|character|character.upcase }.join
p "word: " + word
p "result: " + result
end
p words.join(" ")
You will see here that it actually works because I'm assigning the result of the loop to a new variable result. Then, result is passed back to the original map! call instead of word.
"word: sdsdasda"
"result: SDSDASDA"
"word: asdas"
"result: ASDAS"
"word: asdas"
"result: ASDAS"
If you run this you will see in this case that the end result prints ["result: SDSDASDA", "result: ASDAS", "result: ASDAS"]. That is because the last line of my loop is p "result: " + result.
I am working on a caesar cipher which is a real simple cipher which shifts each letter in a message to the right in accordance with a given key. For example, with a key of 3, the message "hello" would become encrypted as "ifmmp"
I have written this program as a series of loops which are... I forgot the term, but its where you have a loop inside of a loop. The term escapes me at the moment.
Anyway, the way I am doing this is by first converting the message, which might consist of several statements, into an array of words.
Then, I am converting each of those words into an array of letters, so that I can shift them individually.
Finally, I am merging the array of letters into a single words, and I am merging the array of words back into a single message.
The problem I am running into is that whenever I am trying to use the map and map! methods, I cannot get the shifted letters to retain their value. I come from a C/C++ background, and in those languages I wouldn't have a problem with doing this because I understand how pointers and references work, but I don't know how this works in Ruby.
My question is: How can I get the values of an array to be changed inside of a loop, and not reset back to their original values once I exit the loop? The commented code is as follows:
def caesar_cipher(message,key)
#Convert message to array
message = message.split(' ')
#Map each word in the array to the cipher method
message.map! do |word|
puts "message is: #{message} and the current word is: #{word}"
#Split each word into an array of characters
word = word.split('')
puts "after splitting word is: #{word.inspect}"
#Map each letter to cipher function
word.map do |letter|
puts "trying to shift the letter: #{letter.inspect}"
#Based on the value of the key, each letter will be shifted to the right key times
key.times do
#Cases when the letter is at the end of the alphabet
case letter
when "z"
letter = "a"
when "Z"
letter = "A"
#By default, each letter will be shifted to the next letter in the alphabet per each iteration of the loop
else
letter = letter.next!
end
puts "the letter is now: #{letter.inspect}"
end
#Join the array of letters back into a single word
word = word.join('')
puts "after joining word is: #{word.inspect}"
end
end
#Join the array of words back into the shifted message
message.join(' ')
end
Your code was mostly fine. I made just two tiny fixes
def caesar_cipher(message,key)
message = message.split(' ')
message.map! do |word|
word = word.split('')
word.map! do |letter| # or word = word.map
key.times do
case letter
when "z"
letter = "a"
when "Z"
letter = "A"
else
letter = letter.next!
end
end
letter # return the next letter from the block
end
word.join('')
end
message.join(' ')
end
puts caesar_cipher('hello', 2)
# >> jgnnq
What you were doing wrong
The values were not retaining changes because you didn't save them (map doesn't change the original array, it returns a changed copy)
Sometimes, return value of word.map was letter.next! (because it was the last expression evaluated in the block), which is a number, not a letter. You need to always return the letter.
Not a direct answer to the question, but you might find a more functional approach useful.
I try to reduce nested loops and conditional branch logic where possible, as they can be quite painful to follow.
def caesar_cipher(message, key)
key.times do
message = message
.split("")
.map(&:ord) # convert each character to ascii number
.map(&:next) # increment ascii number by 1
.map(&:chr) # convert ascii number back to character
.join
.gsub("{", "a") # fix characters out of range
.gsub("[", "A")
end
message
end
This code capitalizes the first letter of each word in a string.
Eg "this is a sentence" becomes "This Is A Sentence".
def capitalize_words(string)
words = string.split(" ")
idx = 0
while idx < words.length
word = words[idx]
word[0] = word[0].upcase
words[idx] = word #this line of code can be made redundant, but why?
idx += 1
end
return words.join(" ")
end
In the while statement, I don't understand why the third line is unnecessary. The second line sets the first letter of a word to capital:
word[0] = word[0].upcase
how does the while statement know to refer back to the previous line
word = words[idx]
to put the new capitalised-letter word back into the words array? I thought that when codes are executed, it always works in a forward fashion, please let me know if this understanding is incorrect.
It's because word variable holds reference for object - the same object that is in words array. So if you modify this object, the object in array is modified also, because it's the same.
BTW what you're trying to do here can be done much easier:
string.split(' ').map(&:capitalize).join(' ')
As Stefan suggested: Keep in mind that capitalize not only converts first character to uppercase, but also converts all remaining chars to lowercase. If this is not what you want, you can also do:
string.split(' ').map { |word| word.slice(0, 1).upcase + word.slice(1..-1) }
or use Stefan's solution with regexp:
string.gsub(/\b\w/) { |ch| ch.upcase }
Keep in mind that \b in regexp will 'split' your word not only by spaces, but by any word boudary.
If you are only using ruby then use answer as per #Marek's answer :
string.split(' ').map(&:capitalize).join(' ')
and If you are using Ruby with Rails then use this:
"this is a sentence".titleize
I am trying to write a method that takes in a string that will have no spaces. All I want it to do is to return "bug" when the character in the string is a letter [A-Za-z] and to return "ant" if it's any other character.
def Letter(str)
(0..str.length).to_a do |index|
if str[index].chr =~ /[A-Za-z]/ ##I think this is where things are going wrong.
puts "bug"
else
puts "ant"
end
end
end
Does anyone have any idea how to fix this? I keep getting arrays of consecutive numbers.
Rewritten
def letter(str)
str.each_char.map do |char|
(char =~ /[[:alpha:]]/) ? 'bug' : 'ant'
end
end
In your code, you are trying to print "bug" or "ant"; but you're returning (0..str.length).to_a. This function will return an array of bugs and ants. It is also made more Rubyish:
methods should be in snake_case (lowercase, with underscores between words)
iterating over strings is easier with each_char
it's fine with [A-Za-z], but [[:alpha:]] is both clearer and handles Unicode stuff.
since we're testing each character, you know it's going to be one character long, so you don't need the start of line and end of line anchors.
def letter(str)
str.chars.each do |x|
puts x=~ /[A-Za-z]/ ? "bug" : "ant"
end
end
First things first, in your loop your are trying to convert a range
into an array.
(0..str.length).to_a
str.length returns a number, therefore making it into an array will give you an array of numbers. Hence your problem.
Second, you have to have brackets around your /a-zA-Z/ regex
Third, use the ternary operator. It's great for small if statements. Heres the syntax:
boolean ? "if boolean is true this code will execute" : "else this code will"
Fourth, use the .each methods, ruby is loved partly because of the simplicity of loops and iterating!
Happy coding!
I tried to write a function which will be able to randomly change letters in word except first and last one.
def fun(string)
z=0
s=string.size
tab=string
a=(1...s-1).to_a.sample s-1
for i in 1...(s-1)
puts tab[i].replace(string[a[z]])
z=z+1
end
puts tab
end
fun("sample")
My output is:
p
l
a
m
sample
Anybody know how to make it my tab be correct?
it seems to change in for block, because in output was 'plamp' so it's random as I wanted but if I want to print the whole word (splampe) it doesn't working. :(
What about:
def fun(string)
first, *middle, last = string.chars
[first, middle.shuffle, last].join
end
fun("sample") #=> "smalpe"
s = 'sample'
[s[0], s[1..-2].chars.shuffle, s[-1]].join
# => "slpmae"
Here is my solution:
def fun(string)
first = string[0]
last = string[-1]
middle = string[1..-2]
puts "#{first}#{middle.split('').shuffle.join}#{last}"
end
fun('sample')
there are some problems with your function. First, when you say tab=string, tab is now a reference to string, so, when you change characters on tab you change the string characters too. I think that for clarity is better to keep the index of sample (1....n)to reference the position in the original array.
I suggest the usage of tab as a new array.
def fun(string)
if string.length <= 2
return
z=1
s=string.size
tab = []
tab[0] = string[0]
a=(1...s-1).to_a.sample(s-1)
(1...s-1).to_a.each do |i|
tab[z] = string[a[i - 1]]
z=z+1
end
tab.push string[string.size-1]
tab.join('')
end
fun("sample")
=> "spalme"
Another way, using String#gsub with a block:
def inner_jumble(str)
str.sub(/(?<=\w)\w{2,}(?=\w)/) { |s| s.chars.shuffle.join }
end
inner_jumble("pneumonoultramicroscopicsilicovolcanoconiosis") # *
#=> "poovcanaiimsllinoonroinuicclprsciscuoooomtces"
inner_jumble("what ho, fellow coders?")
#=> "waht ho, folelw coedrs?"
(?<=\w) is a ("zero-width") positive look-behind that requires the match to immediately follow a word character.
(?=\w) is a ("zero-width") positive look-ahead that requires the match to be followed immediately by a word character.
You could use \w\w+ in place of \w{2,} for matching two or more consecutive word characters.
If you only want it to apply to individual words, you can use gsub or sub.
*A lung disease caused by inhaling very fine ash and sand dust, supposedly the longest word in some English dictionaries.