How to Find the Middle Character(s) in Ruby? - ruby-on-rails

I'm trying to write a method in Ruby that:
if the string length is even numbers it will return the middle two characters and,
if the string length is odd it will return only the middle character
i put together this code, but it is not working:
def the_middle(s)
if s.length % 2 == 0
return s.index(string.length/2-1) && s.index(string.length/2)
else
return s.index(string.length/2).round
end
end
I think the problem is in the syntax, not the logic, and I was hoping someone could identify where the syntax error might be.
I really appreciate your help!

Actually you have both syntax errors and logic (semantic) errors in that code.
First of all it seems you have misunderstood how the index method on string works. It does not return the character at the given index but the index of a given substring or regex as can be seen in the documentation:
Returns the index of the first occurrence of the given substring or pattern (regexp) in str.
You're also using the wrong operator to concatenate the two middle characters when the string length is even. && is the logical and operator. It's usually used for conditions and not assigments - for example in an if statement if s.length.even? && s.length > 2. The operator you want to use is + which concatenates strings.
Finally, you're using string.length but string is not defined anywhere. What you mean is probably s.length (the input parameter).
The correct solution would be more like the following:
def the_middle(s)
if s.length.even?
return s[s.length/2-1] + s[s.length/2]
else
return s[s.length/2]
end
end
I have taken the liberty to replace s.length % 2 == 0 with s.length.even? as it's more intention revealing and really the ruby way of finding out whether an integer is even or odd.

You can solve this without a conditional using String#[].
Using a range with a negative end:
def the_middle(s)
i = (s.length - 1) / 2
s[i..-i.succ]
end
Or start and length:
def the_middle(s)
a, b = (s.length - 1).divmod(2)
s[a, b + 1]
end
Both return the same results:
the_middle("a") #=> "a"
the_middle("aba") #=> "b"
the_middle("abcba") #=> "c"
the_middle("abcdcda") #=> "d"
# ^
the_middle("abba") #=> "bb"
the_middle("abccba") #=> "cc"
the_middle("abcddcda") #=> "dd"
# ^^

Try this:
def get_middle(s)
x = (s.length/2)
s.length.even? ? s[x-1..x] : s[x]
end

Since olerass already answered your doubt about the syntax, i will suggest you a less verbose solution for the question in the title:
def the_middle(s)
return s[s.length/2] if s.length.odd?
s[s.length/2-1] + s[s.length/2]
end

Same answer the syntax is just consolidated.
Format (logic result) ? ( if true this is the result) : (if false this is the result)
def get_middle(s)
num = s.length
num.even? ? ( s[num/2-1] + s[num/2]) : (s[num/2])
end

Related

I know how to reverse a string in ruby without .reverse, but is there a way to only reverse odd indexed characters?

I know how I can reverse a string in ruby without the reverse method
def reverse(string)
string.each_char.inject(""){|str, char| str.insert(0, char) }
end
puts reverse('hello world')
but is there a way I can reverse only the odd indices to look like this.
output: hlloo wlred
that's an interesting problem, here's what I came up with:
def funky_reverse(str)
out = ""
str.length.times{|i| out+= i.even? ? str[i] : str[-i-1]}
out
end
Here's how I'd do it:
def odd_reverse(str)
a = [str, str.reverse]
str.size.times.map {|i| a[i % 2][i] }.join
end
odd_reverse("hello world")
# => "hlloo wlred"
This is pretty simple. For each character index i it alternates (i % 2) taking the next char from either the string or its reverse ([s, s.reverse]).
Working off of Les Nightingill's answer I came up with this which handles both odd and even length strings using the reference_index variable to point to the end of the string or slightly past it as needed.
def funky_reverse(str)
out = ''
reference_index = str.length.odd? ? str.length - 1 : str.length
str.length.times{ |i| out += i.even? ? str[i] : str[reference_index - i] }
out
end
> funky_reverse('hello world')
=> "hlloo wlred"
> funky_reverse('hello world!')
=> "h!lloow rlde"
This looks like a homework question? :D

Simpler way to alternate upper and lower case words in a string

I recently solved this problem, but felt there is a simpler way to do it. I looked into inject, step, and map, but couldn't figure out how to implement them into this code. I want to use fewer lines of code than I am now. I'm new to ruby so if the answer is simple I'd love to add it to my toolbag. Thank you in advance.
goal: accept a sentence string as an arg, and return the sentence with words alternating between uppercase and lowercase
def alternating_case(str)
newstr = []
words = str.split
words.each.with_index do |word, i|
if i.even?
newstr << word.upcase
else
newstr << word.downcase
end
end
newstr.join(" ")
end
You could reduce the number of lines in the each_with_index block by using a ternary conditional (true/false ? value_if_true : value_if_false):
words.each.with_index do |word, i|
newstr << i.even? ? word.upcase : word.downcase
end
As for a different way altogether, you could iterate over the initial string, letter-by-letter, and then change the method when you hit a space:
def alternating_case(str)
#downcase = true
new_str = str.map { |letter| set_case(letter)}
end
def set_case(letter)
#downcase != #downcase if letter == ' '
return #downcase ? letter.downcase : letter.upcase
end
We can achieve this by using ruby's Array#cycle.
Array#cycle returns an Enumerator object which calls block for each element of enum repeatedly n times or forever if none or nil is given.
cycle_enum = [:upcase, :downcase].cycle
#=> #<Enumerator: [:upcase, :downcase]:cycle>
5.times.map { cycle_enum.next }
#=> [:upcase, :downcase, :upcase, :downcase, :upcase]
Now, using the above we can write it as following:
word = "dummyword"
cycle_enum = [:upcase, :downcase].cycle
word.chars.map { |c| c.public_send(cycle_enum.next) }.join("")
#=> "DuMmYwOrD"
Note: If you are new to ruby, you may not be familiar with public_send or Enumberable module. You can use the following references.
Enumberable#cycle
#send & #public_send

Reverse a String in Ruby by Reading Backwards

I'm just practicing and learning while loops and conditionals (not yet into arrays). I'm trying to reverse any string by concatenating letters, starting from the last letter of the word to the first. eg., for cat, start at t, then a, then c to get tac.
I don't get what's wrong in the code. I'm wondering why the 6th line ( reverse += letter) gives the error message:
6: in `+': no implicit conversion of nil into String (TypeError)
What's being nil'ed?
def is_reversed(word)
i = word.length
reverse = ""
while i > word.length || i != -1
letter = word[i]
reverse += letter
i = i - 1
end
return reverse
end
puts is_reversed("cat")
There are a few issue here but the bottom line is that you're looping wrong. You can either count up to a number or down from a number and it looks like you want to count down by starting at word.length. That's fine, but let's look and see what you're actually doing.
With while i > word.length || i != -1 you're checking each iteration that i is...greater than the length of the word? How would it get that way (you're not adding to i anywhere) and why would you want to check that?
Since you chose to count down, we want to stop when there are no letters remaining. So change your condition to while i > 0. Now we will loop only while there are letters left to go through.
There's another problem though - because indices start at 0, trying to get word[i] when i == 3 will get you nil! So you actually want to move the i = i - 1 to be the first line within your loop.
After these changes, you should have:
def is_reversed(word)
i = word.length
reverse = ""
while i > 0
i = i - 1
letter = word[i]
reverse += letter
end
return reverse
end
puts is_reversed("cat")
def is_reversed(word)
i = word.length
reverse = ""
while i > word.length || i != -1
i = i - 1
letter = word[i]
reverse += letter
end
return reverse
end
puts is_reversed("cat")
// will return 'tact'
Very first time in the loop above you're trying to find the letter you're putting the index as word.length which actually doesn't exist & hence returns nil which threw the error.
To get the last letter of a string you'll have to do i = i - 1 before you do anything else inside the loop.
Second, I think your condition is flawed. If you try to find the element at -1 in an array or string in ruby it will give you the last element.
And the first condition i > word.length will never satisfy as i's value is word.length.
So you can do something like this
def is_reversed(word)
i = word.length
reverse = ""
while i > 0
i = i - 1
letter = word[i]
reverse += letter
end
return reverse
end
puts is_reversed("cat")
//returns 'tac'
I realize this has been answered, but how about something more like:
def is_reversed(w)
w.split("").each_with_object("").with_index{ |(l,a), i| a << w[(w.length-1-i)] }
end
In console:
is_reversed('this is reversed')
=> "desrever si siht"
is_reversed('and so is this')
=> "siht si os dna"
is_reversed('antidisestablishmentarianism')
=> "msinairatnemhsilbatsesiditna"
Notes:
105 characters instead of 152
3 lines instead of 10
Uses << instead of += (which is faster)
Avoids the while i > 0 bit which is, IMO, not very idiomatic Ruby
Avoids unnecessary variable assignments (i = word.length, reverse = "", i = i - 1, and letter = word[i])

How to merge 2 strings alternately in rails?

I have 2 strings:
a = "qwer"
b = "asd"
Result = "qawsedr"
Same is the length of b is greater than a. show alternate the characters.
What is the best way to do this? Should I use loop?
You can get the chars from your a and b string to work with them as arrays and then "merge" them using zip, then join them.
In the case of strings with different length, the array values must be reversed, so:
def merge_alternately(a, b)
a = a.chars
b = b.chars
if a.length >= b.length
a.zip(b)
else
array = b.zip(a)
array.map{|e| e != array[-1] ? e.reverse : e}
end
end
p merge_alternately('abc', 'def').join
# => "adbecf"
p merge_alternately('ab', 'zsd').join
# => "azbsd"
p merge_alternately('qwer', 'asd').join
# => "qawsedr"
Sebastián's answer gets the job done, but it's needlessly complex. Here's an alternative:
def merge_alternately(a, b)
len = [a.size, b.size].max
Array.new(len) {|n| [ a[n], b[n] ] }.join
end
merge_alternately("ab", "zsd")
# => "azbsd"
The first line gets the size of the longer string. The second line uses the block form of the Array constructor; it yields the indexes from 0 to len-1 to the block, resulting in an array like [["a", "z"], ["b", "s"], [nil, "d"]]. join turns it into a string, conveniently calling to_s on each item, which turns nil into "".
Here's another version that does basically the same thing, but skips the intermediate arrays:
def merge_alternately(a, b)
len = [a.size, b.size].max
len.times.reduce("") {|s, i| s + a[i].to_s + b[i].to_s }
end
len.times yields an Enumerator that yields the indexes from 0 to len-1. reduce starts with an empty string s and in each iteration appends the next characters from a and b (or ""—nil.to_s—if a string runs out of characters).
You can see both on repl.it: https://repl.it/I6c8/1
Just for fun, here's a couple more solutions. This one works a lot like Sebastián's solution, but pads the first array of characters with nils if it's shorter than the second:
def merge_alternately(a, b)
a, b = a.chars, b.chars
a[b.size - 1] = nil if a.size < b.size
a.zip(b).join
end
And it wouldn't be a Ruby answer without a little gsub:
def merge_alternately2(a, b)
if a.size < b.size
b.gsub(/./) { a[$`.size].to_s + $& }
else
a.gsub(/./) { $& + b[$`.size].to_s }
end
end
See these two on repl.it: https://repl.it/I6c8/2

What is the meaning of '<==>' in Ruby? [duplicate]

This question already has answers here:
What is the Ruby <=> (spaceship) operator?
(6 answers)
Closed 9 years ago.
What is the meaning of '<==>' in Ruby?
Example: The code comes from the following class that compares numbers in the format x.x.x,
def <==>(other)
# Some code here
end
The following code comes from this class that orders numbers like x.x.x,
class Version
attr_reader :fst, :snd, :trd
def initialize(version="")
v = version.split(".")
#fst = v[0].to_i
#snd = v[1].to_i
#trd = v[2].to_i
end
def <=>(other)
return #fst <=> other.fst if ((#fst <=> other.fst) != 0)
return #snd <=> other.snd if ((#snd <=> other.snd) != 0)
return #trd <=> other.trd if ((#trd <=> other.trd) != 0)
end
def self.sort
self.sort!{|a,b| a <=> b}
end
def to_s
#sorted = #fst.to_s + "." + #snd.to_s + "." + #trd.to_s
#Puts out "#{#sorted}".
end
end
That is the spaceship operator. However, it is actually <=> (not <==>).
Although that is not its official name, I'm sure, it's the most commonly used name for that operator. It is a comparison operator where
If other is less than self, return 1,
If other is equal to self, return 0
If other is greater than self, return -1
It is a powerful operator in that by just implementing this you can do sorting of your own type and participate in a lot of other niceties, like the Enumerable mixin.
Why don't you just try it out? By just typing in the code you posted, it is trivial to see for yourself that it doesn't mean anything, since <==> is not a valid method name in Ruby. The code you posted will just raise a SyntaxError.

Resources