Camelize up to a certain part of a string - ruby-on-rails

I have:
s = "like_so__case"
camelize gives this:
s.camelize # => "LikeSoCase"
I'm looking for conversion up to a double underscore __ to get:
"LikeSo__case"
How can I camelize only up to a certain part of a string?

The simplest option is to gsub part of your string.
'like_so__case'.gsub(/(.*?)(__.*)/) { "#{$1.camelize}#{$2}" }
#=> "LikeSo__case"
UPDATE
Cleaner and faster way arising from comments.
'like_so__case__h'.sub(/(.*?)(?=__)/, &:camelize)
#=> "LikeSo__case__h"

s = "like_so__case"
=> "like_so__case"
s.split('__', 2).tap { |s| s[0] = s[0].camelize }.join('__')
=> "LikeSo__case"
You of course could wrap it in string method

For getting this LikeSo__case, we can do like:
s="like_so__case"
s.split('__').tap { |s| s[0] = s[0].camelize }.join('__')

Your description on the demand is not so clear.
From your excepted result, I understand it as 'camelize a part of string until a pattern'. I should note one thing first that camelize is not part of Ruby's standard library of class String. ActiveSupport::Inflector provides it.
So if you want to just camelize each part divided by a pattern, use str.split('_').map(&:capitalize).join('_'). In your case, it returns 'Like_So__Case'.
Ruby's String has another instance method named partition, which splits the string into three parts (an array):
Part before the pattern
The pattern
Part after the pattern
So str.partition('__').tap { |a| a[0] = a[0].split('_').map(&:capitalize).join }.join should be your answer in plain Ruby.

No need of relying on camelize. Simply, this:
"like_so__case"
.gsub(/_?([a-z])([a-z]*)(?=.*__)/i){$1.upcase + $2.downcase}
# => "LikeSo__case"

def camelize(s)
for i in 0..s.size-2 do
if s[i] == "_" and s[i+1] == "_"
next
elsif s[i] == "_" and s[i+1] != "_" and s[i-1] != "_"
s[i+1] = s[i+1].upcase
s[i] = ""
else
next
end
end
return s
end
Use this method to solve your problem

s = "like_so__case"
i = s.index('__')
#=> 7
s.tap { |s| s[0,i] = s[0,i].camelize }
#=> LikeSo__case
The last line could be replaced by two lines:
s[0,i] = s[0,i].camelize
s
If the original string is not to be mutated write
s.dup.tap { |s| s[0,i] = s[0,i].camelize }

Related

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

How to check if each word in an array contains a substring and reject those in Ruby on Rails?

a = ["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"]
a = a.reject { |x| x.in? ["GOOD", "VALID"]}
#=> ["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"]
I don't want words that contain substring VALID or GOOD.
Output should be ["SUPER"] only.
grep_v would work:
a = ["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"]
a = a.grep_v(/GOOD|VALID/)
#=> ["SUPER"]
You could say this:
a = a.reject { |x| x.include?("GOOD") || x.include?("VALID")}
What in? does is to check if the receiver is present in the array passed as argument, meaning:
1.in?([1, 2]) # true
3.in?([1, 2]) # false
That's to say, it checks for the "whole" object, rather than a part of it.
If you want to reject the elements in your array that match with VALID and/or GOOD, you can use =~:
["SUPER", "SOME_VALID", "ROME_INVALID", "SUPER_GOOD"].reject { |word| word =~ /VALID|GOOD/ } # ["SUPER"]
Notice, this is also going to reject words like "VALIDITY", "GOODNESS", etc.
You could use include?:
a.reject { |x| ["GOOD", "VALID"].any?{ |word| x.include?(word) } }
#=> ["SUPER"]

Append suffix to string only if it is present (or not nil)

I am looking for a concise way as an alternative to this
value = if my_var.present? # or .nil? I dont care
"#{my_var} %"
end
Concise meaning at least that no if statement is used and the variable is only written once.
Kind of like this question how to append a string to a variable that either exists or not?
Just the other way around: When the value is not existing, then the string with the suffix makes no sense to show.
string.presence&.concat('suffix')
In case you care for nils only and not overall presence, you can simplify it:
string&.concat('suffix')
If you don't want to mutate the original value, you can instead do the somewhat cryptic:
string&.+('suffix')
Good old try:
'foo'.try(:+, ' %') #=> "foo %"
nil.try(:+, ' %') #=> nil
or with a block:
'foo'.try { |s| "#{s} %" } #=> "foo %"
nil.try { |s| "#{s} %" } #=> nil
In order to handle blank strings, you can just add presence:
' '.presence.try { |s| "#{s} %" } #=> nil
Also:
value = my_var && "#{my_var} %"
value = my_var.dup.presence.tap { |s| s << " %" if s }

How to Find the Middle Character(s) in Ruby?

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

Easier way to write If hash includes then - Ruby

I have the following in an initialize method on my model:
#home_phone = contact_hash.fetch('HomePhone')
However, sometimes I need this instead:
#home_phone = contact_hash.fetch('number')
Also, sometimes neither of those will be true and I will need the home_phone attribute to be empty.
How can I write this out without creating a big loop like so:
if contact_hash.has_key?('HomePhone')
#home_phone = contact_hash.fetch('HomePhone')
elsif contact_hash.has_key?('number')
#home_phone = contact_hash.fetch('number')
else
#home_phone = ""
end
You could try
#home_phone = contact_hash.fetch('HomePhone', contact_hash.fetch('number', ""))
or better
#home_phone = contact_hash['HomePhone'] || contact_hash['number'] || ""
contact_hash.values_at('HomePhone','number','home_phone').compact.first
Edit:
My first solution did not really give the answer asked for. Here is a modified version, although I think in the case of only 3 options the solution given by #knut is better.
contact_hash.values_at('HomePhone','number').push('').compact.first
def doit(h, *args)
args.each {|a| return h[a] if h[a]}
""
end
contact_hash = {'Almost HomePhone'=>1, 'number'=>7}
doit(contact_hash, 'HomePhone', 'number') # => 7
You could use values_at I suppose:
#home_phone = contact_hash.values_at('HomePhone', 'number').find(&:present?).to_s
That isn't exactly shorter but it wouldn't be convenient if you had the keys in an array:
try_these = %w[HomePhone number]
#home_phone = contact_hash.values_at(*try_these).find(&:present?).to_s
You could also wrap that up in a utility method somewhere or patch it into Hash.

Resources