Ruby - Converting Strings to CamelCase - ruby

I'm working on an exercise from Codewars. The exercise is to convert a string into camel case. For example, if I had
the-stealth-warrior
I need to convert it to
theStealthWarrior
Here is my code
def to_camel_case(str)
words = str.split('-')
a = words.first
b = words[1..words.length - 1].map{|x|x.capitalize}
new_array = []
new_array << a << b
return new_array.flatten.join('')
end
I just tested it out in IRB and it works, but in codewars, it won't let me pass. I get this error message.
NoMethodError: undefined method 'map' for nil:NilClass
I don't understand this, my method was definitely right.

You need to think about edge cases. In particular in this situation if the input was an empty string, then words would be [], and so words[1..words.length - 1] would be nil.
Codewars is probably testing your code with a range of inputs, including the emplty string (and likely other odd cases).

The following works in Ruby 2 for me, even if there's only one word in the input string:
def to_camel_case(str)
str.split('-').map.with_index{|x,i| i > 0 ? x.capitalize : x}.join
end
to_camel_case("the-stealth-warrior") # => "theStealthWarrior"
to_camel_case("warrior") # => "warrior"
I know .with_index exists in 1.9.3, but I can't promise it will work with earlier versions of Ruby.

A simpler way to change the text is :
irb(main):022:0> 'the-stealth-warrior'.gsub('-', '_').camelize
=> "TheStealthWarrior"

This should do the trick:
str.gsub("_","-").split('-').map.with_index{|x,i| i > 0 ? x.capitalize : x}.join
It accounts for words with underscores

Maybe there's a test case with only one word?
In that case, you'd be trying to do a map on words[1..0], which is nil.
Add logic to handle that case and you should be fine.

Related

Capitalizing Vowels in a String with Ruby

I have a method that takes in a string as an argument, replaces each letter with the next letter in the alphabet and then capitalizes every vowel. I have gotten both of those to work individually (the replacing and capitalization), but at this point, I just don't know how to make them work together.
def LetterChanges(str)
new_str = str.downcase.split("")
new_str.each do |x|
x.next!
end
new_str.to_s.tr!('aeiou','AEIOU')
return new_str.join("")
end
LetterChanges("abcdef")
new_str.to_s is not stored anywhere. It doesn't affect the original array.
return new_str.join("").tr('aeiou', 'AEIOU')
This will convert the array back to a string and you can operate on that and return it.
That could be resolved with gsub.
"abcdef".gsub(/./){|char| char.next}.gsub(/[aeiou]/){|vowel| vowel.upcase}
#=> "bcdEfg"
so that method could be
def letter_changes_gsub(str)
str.gsub(/./){|char| char.next}.gsub(/[aeiou]/){|vowel| vowel.upcase}
end
That is faster and more simple that work with arrays.
Other answers already showed you how to combine both parts of your code. But there's another issue: String#next is continuing witch "aa" after "z":
"z".next #=> "aa"
You could add an if statement to handle this case:
str.chars.map do |char|
if char == 'z'
'a'
else
char.next
end
end.join
or:
str.chars.map { |char| char == 'z' ? 'a' : char.next }.join
But there's a much simpler way: let String#tr perform the entire substitution:
str.downcase.tr('a-z', 'bcdEfghIjklmnOpqrstUvwxyzA')
Or slightly shorter:
str.downcase.tr('a-z', 'bcdEfghIjk-nOp-tUv-zA')
2.1.0 :012 > 'abcdef'.split('').map(&:next).join.tr('aeiou', 'AEIOU')
=> "bcdEfg"
I would not recommend doing this in one line, of course. But to get at your confusion of how these methods might string together, here is one solution that works. When in doubt, use IRB to call each method and watch how Ruby responds. That will help you figure out where your code is breaking down.
In practice, I would break this into multiple methods. It's too many things for one method to do. And also a lot harder to find bugs (and test), as you found out.
def rotate(string)
string.split('').map(&:next).join
end
def capitalize_vowels(string)
string.tr('aeiou', 'AEIOU')
end
How about:
def string_thing(string)
string.downcase.tr('abcdefghijklmnopqrstuvwxyz','bcdEfghIjklmnOpqrstUvwxyzA')
end
#tr just will replace each character in the first parameter with the corresponding one in the second parameter.
This can be achieved with the combination of gsub and tr:
"abcdef".gsub(/[A-z]/) { |char| char.next }.tr('aeiou', 'AEIOU')
#=> "bcdEfg"
"Fun times!".gsub(/[A-z]/) { |char| char.next }.tr('aeiou', 'AEIOU')
#=> "GvO Ujnft!"

Lazy-evaluation of a "#{}"-string in ruby

I started to put print-statements throughout my code. So as not to clutter up the output, I did something like:
dputs LEVEL, "string"
where LEVEL is 0 for errors, 1 for important .. 5 for verbose and is compared to DEBUG_LEVEL. Now my problem is, that in a statement like:
dputs 5, "#{big_class.inspect}"
the string is always evaluated, also if I set DEBUG_LEVEL to 1. And this evaluation can take a long time. My favourite solution would be something like:
dputs 5, '#{big_class.inspect}'
and then evaluate the string if desired. But I don't manage to get the string in a form I can evaluate. So the only think I could come up with is:
dputs( 5 ){ "#{big_class.inspect}" }
but this looks just ugly. So how can I evaluate a '#{}' string?
You could do this by having dputs use sprintf (via %). That way it can decide not to build the interpolated string unless it knows it's going to print it:
def dputs(level, format_str, *vars)
puts(format_str % vars) if level <= LEVEL
end
LEVEL = 5
name = 'Andrew'
dputs 5, 'hello %s', name
#=> hello Andrew
Or, as you suggest, you can pass a block which would defer the interpolation till the block actually runs:
def dputs(level, &string)
raise ArgumentError.new('block required') unless block_given?
puts string.call if level <= LEVEL
end
I think it's of no value whatsoever, but I just came up with:
2.3.1 :001 > s = '#{a}'
=> "\#{a}"
2.3.1 :002 > a = 1
=> 1
2.3.1 :003 > instance_eval s.inspect.gsub('\\', '')
=> "1"
2.3.1 :004 > s = 'Hello #{a} and #{a+1}!'
=> "Hello \#{a} and \#{a+1}!"
2.3.1 :005 > instance_eval s.inspect.gsub('\\', '')
=> "Hello 1 and 2!"
Don't use that in production :)
I don't think you can dodge the ugly there. The interpolation happens before the call to dputs unless you put it inside a block, which postpones it until dputs evaluates it. I don't know where dputs comes from, so I'm not sure what its semantics are, but my guess is the block would get you the lazy evaluation you want. Not pretty, but it does the job.
OK, obviously I was just too lazy. I thought there must be a more clean way to do this, Ruby being the best programming language and all ;) To evaluate a string like
a = '#{1+1} some text #{big_class.inspect}'
only when needed, I didn't find a better way than going through the string and eval all "#{}" encountered:
str = ""
"#{b}\#{}".scan( /(.*?)(#\{[^\}]*\})/ ){
str += $1
str += eval( $2[2..-2] ).to_s
}
if you're not into clarity, you can get rid of the temporary-variable str:
"#{b}\#{}".scan( /(.*?)(#\{[^\}]*\})/ ).collect{|c|
c[0] + eval( c[1][2..-2] ).to_s
}.join
The String.scan-method goes through every '#{}'-block, as there might be more than one, evaluating it (the 2..-2 cuts out the "#{" and "}") and putting it together with the rest of the string.
For the corner-case of the string not ending with a '#{}'-block, an empty block is added, just to be sure.
But well, after being some years in Ruby, this still feels clunky and C-ish. Perhaps it's time to learn a new language!

Ruby: How to loop through an object that may or may not be an array?

I have an each method that is run on some user-submitted data.
Sometimes it will be an array, other times it won't be.
Example submission:
<numbers>
<number>12345</number>
</numbers>
Another example:
<numbers>
<number>12345</number>
<number>09876</number>
</numbers>
I have been trying to do an each do on that, but when there is only one number I get a TypeError (Symbol as array index) error.
I recently asked a question that was tangentally similar. You can easily force any Ruby object into an array using Array.
p Array([1,2,3]) #-> [1,2,3]
p Array(123) #-> [123]
Of course, arrays respond to each. So if you force everying into an array, your problem should be solved.
A simple workaround is to just check if your object responds to :each; and if not, wrap it in an array.
irb(main):002:0> def foo x
irb(main):003:1> if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
It looks like the problem you want to solve is not the problem you are having.
TypeError (Symbol as array index)
That error tells me that you have an array, but are treating it like a hash and passing in a symbol key when it expects an integer index.
Also, most XML parsers provide child nodes as array, even if there is only one. So this shouldn't be necesary.
In the case of arguments to a method, you can test the object type. This allows you to pass in a single object or an array, and converts to an array only if its not one so you can treat it identically form that point on.
def foo(obj)
obj = [obj] unless obj.is_a?(Array)
do_something_with(obj)
end
Or something a bit cleaner but more cryptic
def foo(obj)
obj = [*obj]
do_something_with(obj)
end
This takes advantage of the splat operator to splat out an array if it is one. So it splats it out (or doesn't change it) and you can then wrap it an array and your good to go.
I was in the same position recently except the object I was working with was either a hash or an array of hashes. If you are using Rails, you can use Array.wrap because Array(hash) converts hashes to an array.
Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
I sometimes use this cheap little trick:
[might_be_an_array].flatten.each { |x| .... }
Use the splat operator:
[*1] # => [1]
[*[1,2]] # => [1,2]
Like Mark said, you're looking for "respond_to?" Another option would be to use the conditional operator like this:
foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);
What are you trying to do with each number?
You should try to avoid using respond_to? message as it is not a very object oriented aproach.
Check if is it possible to find in the xml generator code where it is assigning an integer value when there is just one <"number"> tag and modify it to return an array.
Maybe it is a complex task, but I would try to do this in order to get a better OO design.
I don't know much anything about ruby, but I'd assume you could cast (explicitly) the input to an array - especially given that if the input is simply one element longer it's automatically converted to an array.
Have you tried casting it?
If your input is x, use x.to_a to convert your input into an array.
[1,2,3].to_a
=> [1, 2, 3]
1.to_a
=> [1]
"sample string".to_a
=> ["sample string"]
Edit: Newer versions of Ruby seem to not define a default .to_a for some standard objects anymore. You can always use the "explicit cast" syntax Array(x) to achieve the same effect.

(Ruby) Converting string values into assignable properties for OpenStructs...?

I've got a bit of an odd situation. If I were using a hash, this issue would be easy, however, I'm trying to use "OpenStruct" in Ruby as it provides some decently cool features.
Basically, I think I need to "constantize" a return value. I've got a regular expression:
textopts = OpenStruct.new()
textopts.recipients = []
fileparts = fhandle.read.split("<<-->>")
fileparts[0].chomp.each{|l|
if l =~ /Recipient.*/i
textopts.recipients << $&
elsif l =~ /(ServerAddress.*|EmailAddress.*)/i
textopts.$& = $&.split(":")[1]
end
}
I need a way to turn the $& for "textopts" into a valid property for filling. I've tried "constantize" and some others, but nothing works. I would assume this is possible, but perhaps I'm wrong. Obviously if I were using a hash I could just do "textopts[$&] = .....".
Any ideas?
Keeping the structure of your solution, this is one way to do it:
textopts = OpenStruct.new(:recipients => [])
fileparts = fhandle.read.split('<<-->>')
fileparts.first.chomp.each_line do |l|
case l
when /Recipient.*/i
textopts.recipients << $&
when /(Server|Email)Address.*/i
textopts.send "#{$&}=", $&.split(':')[1]
end
end
But I can't help but think that this should be a proper parser.

What does 'yield called out of block' mean in Ruby?

I'm new to Ruby, and I'm trying the following:
mySet = numOfCuts.times.map{ rand(seqLength) }
but I get the 'yield called out of block' error. I'm not sure what his means. BTW, this question is part of a more general question I asked here.
The problem is that the times method expects to get a block that it will yield control to. However you haven't passed a block to it. There are two ways to solve this. The first is to not use times:
mySet = (1..numOfCuts).map{ rand(seqLength) }
or else pass a block to it:
mySet = []
numOfCuts.times {mySet.push( rand(seqLength) )}
if "numOfCuts" is an integer,
5.times.foo
is invalid
"times" expects a block.
5.times{ code here }
You're combining functions that don't seem to make sense -- if numOfCuts is an integer, then just using times and a block will run the block that many times (though it only returns the original integer:
irb(main):089:0> 2.times {|x| puts x}
0
1
2
map is a function that works on ranges and arrays and returns an array:
irb(main):092:0> (1..3).map { |x| puts x; x+1 }
1
2
3
[2, 3, 4]
I'm not sure what you're trying to achieve with the code - what are you trying to do? (as opposed to asking specifically about what appears to be invalid syntax)
Bingo, I just found out what this is. Its a JRuby bug.
Under MRI
>> 3.times.map
=> [0, 1, 2]
>>
Under JRuby
irb(main):001:0> 3.times.map
LocalJumpError: yield called out of block
from (irb):2:in `times'
from (irb):2:in `signal_status'
irb(main):002:0>
Now, I don't know if MRI (the standard Ruby implementation) is doing the right thing here. It probably should complain that this does not make sense, but when n.times is called in MRI it returns an Enumerator, whereas Jruby complains that it needs a block.
Integer.times expects a block. The error message means the yield statement inside the times method can not be called because you did not give it a block.
As for your code, I think what you are looking for is a range:
(1..5).map{ do something }
Here is thy rubydoc for the Integer.times and Range.

Resources