I'm very new to programming so I'm sorry if I'm asking a really simple question. I've also already done my research and I still can't get what I want so I'm asking here.
So I'm writing a simple camelcase method - All words must have their first letter capitalized without spaces. Right now in order to call this function I have to type camelcase("hello there") which would return "Hello There" in interactive ruby. I am wondering how to convert this method into a different type of method(I think it's called a class method?) which would allow me to do this instead: "hello there".camelcase #=> "Hello There"
I've also seen that the syntax would be like so:
class String
def method()
...
end
end
But I really don't know how to apply it...
def camelcase(string)
newArray = []
newNewArray = []
array = string.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newNewArray = newArray.join(" ")
end
In this way. I've used your_camelcase because I'm not sure if that method does not exist in Ruby String class. Anyway, this is an instance method and you should use self to refer to your string
class String
def your_camelcase
newArray = []
newNewArray = []
array = self.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newArray.join(" ")
end
end
You're almost there. Just put that method into String class. Inside of that method, self will refer to the string. You don't need to (and can't) pass it as a parameter.
class String
def camelcase
newArray = []
newNewArray = []
array = self.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newNewArray = newArray.join(" ")
end
end
'hello there'.camelcase # => "Hello There"
The output you want is not called Camel Case. Camelcase examples are camelCase or CamelCase (no spaces).
If you just want to capitalize each word, it is called Title Case. A naive implementation of titlecase is like so:
class String
def titlecase
self.split.map(&:capitalize).join(" ")
end
end
"hello world".titlecase #=> "Hello World"
Note: to make it a true camelcase implementation, you would replace join(" ") with join.
As you said, each instance you want to change is a string, so we'll need to add the method you want to the String class:
class String
def camelcasify
newArray = []
newNewArray = []
array = string.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newNewArray = newArray.join(" ")
end
end
However... rails comes with a method for this built in, so you shouldn't need to do the above. Try:
"string".camelize
Related
I get the following error when running a simple method that takes in a proper noun string and returns the string properly capitalized.
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
if part[0].upcase
else part[1..-1].downcase
arr << part
end
end
return arr.join(" ")
end
Test cases:
puts format_name("chase WILSON") # => "Chase Wilson"
puts format_name("brian CrAwFoRd scoTT") # => "Brian Crawford Scott"
The only possibility that the above code returns a blank output is because your arr is nil or blank. And the reason your arr is blank(yes it is blank in your case) because of this line of code:
if part[0].upcase
in which the statement would always return true, because with every iteration it would check if the first element of the part string can be upcased or not, which is true.
Hence, your else block never gets executed, even if this got executed this would have returned the same string as the input because you are just putting the plain part into the array arr without any formatting done.
There are some ways you can get the above code working. I'll put two cases:
# one where your map method could work
def format_name(str)
parts = str.split
arr = []
arr = parts.map do |part|
part.capitalize
end
return arr.join(" ")
end
# one where your loop code logic works
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
arr << "#{part[0].upcase}#{part[1..-1].downcase}"
end
return arr.join(" ")
end
There are numerous other ways this could work. I'll also put the one I prefer if I am using just plain ruby:
def format_name(str)
str.split(' ').map(&:capitalize)
end
You could also read more about the Open Classes concept to put this into the String class of ruby
Also, checkout camelize method if you're using rails.
If I have the following:
puts "---doesn't change---"
def change_string(val)
val = "abc"
end
str = "some"
change_string(str)
puts str
puts "---does change---"
def change_string_two(val)
puts "val is of class: #{val.class}"
val << " extra"
puts "val is of class: #{val.class}"
end
change_string_two(str)
puts str
puts "str is of type: #{str.class}"
puts "---does change---"
str = "xxx"
def change_string_three(val)
val.concat(" more things")
end
change_string_three(str)
puts str
It ouptuts:
---doesn't change---
some
---does change---
val is of class: String
val is of class: String
some extra
str is of type: String
---does change---
xxx more things
I understand that it's passing an object reference but I'm confused as to how in one scenario it doesn't change and in two scenarios, it does change. Is this peculiar to strings in Ruby?
This is not peculiar to Ruby. val.concat("x") and val << "x" modify the value val refers to. Whenever you have a function where the argument being passed is a reference, then methods called on the argument which mutate it will result in mutations on the originally referenced thing.
An assignment like val = "abc" on the other hand reassigns the local variable val to refer to something new, and leaves what it previously referred to untouched.
If you want to replace the string rather than add to it, use val.replace("abc"), e.g.
def replace(val)
val.replace("abc")
end
str = "123"
replace(str)
puts str
outputs "abc".
My new method for a string object in ruby is supposed to return a hash of the count of each character within a string (loaded in from a .txt file) and I am probably trying to go about it the easy way, however I can't seem to make it work without passing the object. I was wondering if there was a way to do this without passing a string. Any help would be appreciated.
Here is my code
class String
def frequency
Object.downcase
Object.gsub("\n", " ")
h = {}
h["A:"] = Object.count('a')
h["B:"] = Object.count('b')
h["C:"] = Object.count('c')
h["D:"] = Object.count('d')
h["E:"] = Object.count('e')
h["F:"] = Object.count('f')
h["G:"] = Object.count('g')
h["H:"] = Object.count('h')
h["I:"] = Object.count('i')
h["J:"] = Object.count('j')
h["K:"] = Object.count('k')
h["L:"] = Object.count('l')
h["M:"] = Object.count('m')
h["N:"] = Object.count('n')
h["O:"] = Object.count('o')
h["P:"] = Object.count('p')
h["Q:"] = Object.count('q')
h["R:"] = Object.count('r')
h["S:"] = Object.count('s')
h["T:"] = Object.count('t')
h["U:"] = Object.count('u')
h["V:"] = Object.count('v')
h["W:"] = Object.count('w')
h["K:"] = Object.count('x')
h["Y:"] = Object.count('y')
h["Z"] = Object.count('z')
return h
end
end
Sounds like you are talking about self, which is the ruby keyword that refers to the current object. Note that self is implied if you just call the method. So to use your example
class String
def frequency
count('a')
end
end
would return the number of as in the string
"asdfa".frequency #=> 2
Just a note, but your current method is very repetitive, and you might want to think about taking advantage of a loop to reduce the amount of code. Also you are not counting capital letters :)
Rather than a very long, un-DRY method that iterates your object 26 times, how about using some Ruby:
def frequency
Hash[downcase.gsub(/[^a-z]/,'').chars.group_by(&:to_s).map{|char, group| ["#{char.upcase}:", group.size]}]
end
You can break this apart onto separate lines if you find it easier to read (and to look up the methods in the API [1]):
def frequency
intermediate_variable = downcase
intermediate_variable = intermediate_variable.gsub(/[^a-z]/,'') # only keep a-z characters
intermediate_variable = intermediate_variable.chars.group_by(&:to_s) # split the string into its component characters and then group that array by the characters (run this on an array to see what it does :-) could also have written it `.group_by{|e|e}`
intermediate_variable = intermediate_variable.map{|char, group| ["#{char.upcase}:", group.size]} # map the array of grouped characters into an array of character and counts (formatting the 'character' how you would like your hash key configured
Hash[intermediate_variable] # make a hash of the characters and their counts
end
[1] http://ruby-doc.org/core-2.0.0/Enumerable.html http://ruby-doc.org/core-2.0.0/String.html
Here is the version I used which is a complete copy of the Rosetta Letter Frequency:
#!/usr/bin/env ruby
def letter_frequency(string)
freq = Hash.new(0)
string.each_char.lazy.grep(/[[:alpha:]]/).map(&:upcase).each_with_object(freq) do |char, freq_map|
freq_map[char] += 1
end
end
In ruby you can just open the class and add the method, like:
class String
def my_method
my_method_code
end
end
Then you just call the method string.my_method. However in your case I would rather use a Ruby module. Here is a code sample, very similar to a class but cleaner imho:
#!/usr/bin/env ruby
module MyString
def self.letter_frequency(string)
freq = Hash.new(0)
string.each_char.lazy.grep(/[[:alpha:]]/).map(&:upcase).each_with_object(freq) do |char, freq_map|
freq_map[char] += 1
end
return freq
end
end
p MyString.letter_frequency('absd')
Modules are more suited for adding your own classes into projects avoiding name colliding and creating mixins.
I would just create a hash like this:
class String
def frequency
chars.each_with_object(Hash.new(0)) do |char, h|
h["#{char.upcase}:"] += 1 if char[/[[:alpha:]]/]
end
end
end
I was trying to build a method that you take the first letter of every word and would capitalize it. I wrote it as
def titleize(name)
name.scan(/\w+/) { |x| x.capitalize! }
end
and it just wouldn't work properly. It wouldn't capitalize and letters. I did some searching and found the answer here Capitalizing titles eventually. It was written as
def titleize(name)
name.split(" ").each { |x| x.capitalize! }.join(" ")
end
How come my code didn't capitalize at all though? If I added a put statement and wrote it as
def titleize(name)
name.scan(/\w+/) { |x| puts x.capitalize! }
end
It would output "hi there" with capitals but the => would still be just "hi there" What did I miss?
Corrected code:
def titleize(name)
name.scan(/\w+/).each { |x| x.capitalize! }.join(' ')
end
p titleize("ayan roy") #=>"Ayan Roy"
Let's see why your one not worked:
def titleize(name)
name.scan(/\w+/)
end
p titleize("ayan roy") #=>["ayan", "roy"]
Now your line name.scan(/\w+/) { |x| x.capitalize! } , x is passed as "ayan", "roy". Now look at the below:
def titleize(name)
name.scan(/\w+/) { |x| p x.capitalize! }
end
p titleize("ayan roy")
Output:
"Ayan"
"Roy"
"ayan roy"
As String#scan says:
scan(pattern) {|match, ...| block } → str - if block is given,scan will return the receiver on which it is called. Both forms iterate through str, matching the pattern (which may be a Regexp or a String). For each match, a result is generated and either added to the result array or passed to the block.
scan returns/yields new strings and will never modify the source string. Perhaps you want gsub.
def titleize(name)
name.gsub(/\w+/) {|x| x.capitalize }
end
Or perhaps better to use a likely more correct implementation from the titleize gem.
Your code doesn't work because #scan returns new String objects which are the results of the Regexp and passes them to the block. So in your method you essentially took these new objects, mutated them by calling #capitalize! but never used them anywhere afterwards.
You should do instead:
def titleize(name)
name.scan(/\w+/).each { |x| x.capitalize! }.join(' ')
end
But this seems more readable to me:
def titleize2(name)
name.split(' ').each { |w| w.capitalize! }.join(' ')
end
Note however these methods do not mutate the original argument passed.
The block form of scan returns the original string, regardless of what you do in the block. (I think you may be able to alter the original string in the block by referring directly to it, but it's not recommended to alter the thing you're iterating over.) Instead, do your split variation, but instead of each, do collect followed by join:
name.split(" ").collect { |x| x.capitalize }.join(" ")
This works for titles containing numerals and punctuation, as well.
I know there are a number of ways to create new elements in an existing ruby array.
e.g.
myArray = []
myArray + other_array
myArray << obj
myArray[index] = obj
I'm also pretty sure I could use .collect, .map, .concat, .fill, .replace, .insert, .join, .pack and .push as well to add to or otherwise modify the contents of myArray.
However, I want to ensure that myArray only ever includes valid HTTP/HTTPS URLs.
Can anyone explain how I can enforce that kind of behaviour?
I would create a module that allows you to specify an acceptance block for an array, and then override all the methods you mention (and more, like concat) to pre-filter the argument before calling super. For example:
module LimitedAcceptance
def only_allow(&block)
#only_allow = block
end
def <<( other )
super if #only_allow[ other ]
end
def +( other_array )
super( other_array.select(&#only_allow) )
end
end
require 'uri'
my_array = []
my_array.extend LimitedAcceptance
my_array.only_allow do |item|
uri = item.is_a?(String) && URI.parse(item) rescue nil
uri.class <= URI::HTTP
end
my_array << "http://phrogz.net/"
my_array << "ftp://no.way"
my_array += %w[ ssh://bar http://ruby-lang.org http:// ]
puts my_array
#=> http://phrogz.net/
#=> http://ruby-lang.org
Create a class to encapsulate behavior you want. Then you can create your << method doing the verifications you want.
Put all logic that handle this data in methods in this domain class. Probably you will discover code floating around the use of this data to move to the new class.
My 2 cents.
Use this to insert. (untested).
def insert_to_array(first_array, second_array)
second_array.each do |i| {
if URI.parse(i).class == URI::HTTP
first_array.insert(i)
end
}
first_array
end