Ruby - get first X characters of a string that are numeric - ruby-on-rails

What would be a method that would create these results? I'm not very good at RegEx I'm afraid and couldn't see a relatively straightforward way to achieve this
"100124" => "100124"
"100asdf124" => "100"
"1asdf124" => "1"
"100 124" => "100"
"1 124" => "1"
"100.124" => "100"
"1.124" => "1"
UPDATE
This is NOT the same as extracting numbers from a string! Because I don't wnat to extract ALL numbers from a string, only the FIRST set until some other character or space or punctuation or whatever else that's not a number interrupts it! Added more examples to be clear

Does this method solve your problem?
def numeric_prefix(string)
match = string.match(/^\d+/)
match[0] if match
end

Related

Is there any function to get an array value from string except `eval`?

I have a string params, whose value is "1" or "['1','2','3','4']". By using eval method, I can get the result 1 or [1,2,3,4], but I need the result [1] or [1,2,3,4].
params[:city_id] = eval(params[:city_id])
scope :city, -> (params) { params[:city_id].present? ? where(city_id: (params[:city_id].is_a?(String) ? eval(params[:city_id]) : params[:city_id])) : all }
Here i don't want eval.
scope :city, -> (params) { params[:city_id].present? ? where(city_id: params[:city_id]) : all }
params[:city_id] #should be array values e.g [1] or [1,2,3,4] instead of string
Your strings look very close to JSON, so probably the safest thing you can do is parse the string as JSON. In fact:
JSON.parse("1") => 1
JSON.parse('["1","2","3","4"]') => ["1","2","3","4"]
Now your array uses single quotes. So I would suggest you to do:
Array(JSON.parse(string.gsub("'", '"'))).map(&:to_i)
So, replace the single quotes with doubles, parse as JSON, make sure it's wrapped in an array and convert possible strings in the array to integers.
I've left a comment for what would be my preferred approach: it's unusual to get your params through as you are, and the ideal approach would be to address this. Using eval is definitely a no go - there are some big security concerns to doing so (e.g. imagine someone submitting "City.delete_all" as the param).
As a solution to your immediate problem, you can do this using a regex, scanning for digits:
str = "['1','2','3','4']"
str.scan(/\d+/)
# => ["1", "2", "3"]
str = '1'
str.scan(/\d+/)
# => ["1"]
# In your case:
params[:city_id].scan(/\d+/)
In very simple terms, this looks through the given string for any digits that are in there. Here's a simple Regex101 with results / an explanation: https://regex101.com/r/41yw9C/1.
Rails should take care of converting the fields in your subsequent query (where(city_id: params[:city_id])), though if you explictly want an array of integers, you can append the following (thanks #SergioTulentsev):
params[:city_id].scan(/\d+/).map(&:to_i)
# or in a single loop, though slightly less readable:
[].tap { |result| str.scan(/\d+/) { |match| result << match.to_i } }
# => [1, 2, 3, 4]
Hope that's useful, let me know how you get on or if you have any questions.

Ruby: trim or replace leading digits

I have a string in the following form:
'123string89continues...'
What is the most elegant way to replace or trim the leading digits? Note, there can be more or less than 3 digits, but at least one will always be present.
'1string89continues...' # This can happen
'0123456789string89continues...' # This can happen
'string89continues...' # This cannot happen
'123string89continues...'[/\D.*/]
#⇒ "string89continues..."
Try this one
"123asdads".sub(/A\d+/, "")
=> "asdads"
"1asdads".sub(/A\d+/, "")
=> "asdads"
"asdads".sub(/A\d+/, "")
=> "asdads"
You can use slice! to delete a specific portion from a string:
string = '123string89continues...'
string.slice!(/\A\d+/) #=> "123"
string #=> "string89continues..."

How can I use a regex match as a hash index in Ruby?

I'm new to Ruby and I've run into an issue I can't solve.
I'm trying to use gsub() to match a pattern in a string, then use that match as an index into a hash. So far, I haven't been able to figure it out. Here's some code:
farm = { "pig_num" => 5, "horse_num" => 2, "cow_num" => 4}
assessment = "There are 'pig_num' pigs on this farm"
assessment.gsub(/'(.+?)'/, '\1') # => "There are pig_num pigs on this farm"
assessment.gsub(/'(.+?)'/, farm) # => "There are pigs on this farm"
assessment.gsub(/'(.+?)'/, farm['\1']) # => TypeError: no implicit conversion of nil into String
assessment.gsub(/'(.+?)'/) { |key| farm[key] }
The first call to gsub() shows that I am matching the string I want.
The second call is an attempt to use the gsub(pattern, hash) flavor found at the Ruby documentation site.
The third call is trying to reference the value using the match as an index.
The fourth is some fancy pants way I thought might work using a lambda/proc/block.
What am I doing wrong?
farm = { "pig_num" => 5, "horse_num" => 2, "cow_num" => 4}
assessment = "There are 'pig_num' pigs on this farm"
1
"You may want to get the first object from farm hash but you need to tell from which hash you want to retrieve value". Otherwise, you need to use just Integer with String type like above.
assessment.gsub(/'(.+?)'/, '1')
2
when you 'gsub' string, you get 'pig_num' because you include '' inside the regex so that result would be "'pig_num'". However, the key of hash is "pig_num". "pig_num" and "'pig_num'" are different. That is why you can't get data properly.
assessment.gsub(/'(.+?)'/, farm)
3
You can not point index inside blacket but hash key
assessment.gsub(/'(.+?)'/, farm["pig_num"].to_s)
4
As I said before, you get "'pig_num'" as key. If you print out the key value inside the block, you will see it. You need to change it to 'Pure key'. To get rid of quotation, you can use gsub again and make it empty instead of quotation. gsub! is a destructive method which means modifies original value. If you use just gusb, the method returns modified value but the original key itself (in this situation) does not change. Otherwise, you need to assign the new value to another variable.
assessment.gsub(/'(.+?)'/) { |key| p key.gsub!("\'", ""); farm[key] }
I hope this answer is helpful for you. Cheers
Try this
assessment.gsub(/#{farm.keys.join('|')}/, farm)
I see on your code with regex match it will recognize 'pig_num' is the hash key to find on farm hash. So you need to change your hash like
farm = { "'pig_num'" => 5, "'horse_num'" => 2, "'cow_num'" => 4} or you can change your regex. Example
farm = { "'pig_num'" => 5, "'horse_num'" => 2, "'cow_num'" => 4}
assessment.gsub(/'(.+?)'/, farm) # => "There are 5 pigs on this farm"
Or
farm = { "pig_num" => 5, "horse_num" => 2, "cow_num" => 4}
assessment.gsub(/pig_num/, farm) # => "There are '5' pigs on this farm"
With some minor adjustments, your can use sprintf's %{name} or %<name>snotation:
farm = { pig_num: 5, horse_num: 2, cow_num: 4 }
assessment = "There are '%{pig_num}' pigs on this farm"
sprintf(assessment, farm)
#=> "There are '5' pigs on this farm"

Cannot get exact value on parameter

I have sample parameter below:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"xxxxxxxxxx",
"post" => {
"product_attributes" => {
"name"=>"Ruby",
"product_dtls_attributes" => {
"0"=>{"price"=>"12,333.00"},
"1"=>{"price"=>"111,111.00"}
},
},
"content"=>"Some contents here."
}
Now, the scenario is, I cannot get the price exact value in model.
Instead of:
price = 12,333.00
price = 111,111.00
I get:
price = 12.00
price = 11.00
And now here is what I did in my code:
before_validation(on: :create) do
puts "price = #{self.price}" # I also tried self.price.to_s, but didn't work.
end
UPDATE:
(I am trying do to here is to get the full value and strip the comma).
before_validation(on: :create) do
puts "price = #{self.price.delete(',').to_f}" # I also tried self.price.to_s, but didn't work.
end
Note:
column price is float
The question is, how can I get the exact value of params price.
Thanks!
Looking at the 'price' parameter you provided:
"price"=>"12,333.00"
The problem is with the comma.
For example:
irb(main):003:0> "12,333.00".to_i
=> 12
But you can fix that:
Example:
irb(main):011:0> "12,333.00".tr(",", "_").to_i
=> 12333
The key point is replacing the comma with an underscore. The reason is that 12_333 is the same integer as 12333 (the underscores are ignored). You could just remove the comma with tr(",", "") as well. In this case, you could replace tr with gsub and have the same effect.
By the way, are you aware that your validation method is not doing anything besides printing? Anyway, a before_validation method is not the right approach here because the number will already have been incorrectly converted when the code reaches this point. Instead, you can override the setter on the model:
class MyModel
def price=(new_price)
if new_price.is_a?(String)
new_price = new_price.tr(",", "")
end
super(new_price)
end
end
You can do it like this too:
2.1.1 :002 > "12,333.00".gsub(',', '').to_f
=> 12333.0
This will replace the comma and if you have any decimal value then too it will interpret it:
2.1.1 :003 > "12,333.56".gsub(',', '').to_f
=> 12333.56
The solution I made is to handle it on controller. Iterate the hash then save it. Then it get the proper value which I want to get and save the proper value.
Iterate the following hash and save.
"post" => {
"product_attributes" => {
"name"=>"Ruby",
"product_dtls_attributes" => {
"0"=>{"price"=>"12,333.00"},
"1"=>{"price"=>"111,111.00"}
},
},
"content"=>"Some contents here."
I can't get the full value of price in model because of comma separator. This comma separator and decimal points + decimal places is made by gem.
Price is float, but your data contains a non-numeric character (comma, ","). When the field is converted to a float, parsing likely stops at this character and returns just 12.
I had expected an error to be thrown, though.
I suggest you remove the comma before putting it into the database.

Finding exact words in a string

I have a list of links to clothing websites that I am categorising by gender using keywords. Depending on what website they are for, they all have different URL structures, for example...
www.website1.com/shop/womens/tops/tshirt
www.website2.com/products/womens-tshirt
I cannot use the .include? method because regardless of whether it is .include?("mens") or .include?("womens"), it will return true. How can I have a method that will only return true for "womens" (and vice versa). I suspect it may have to be some sort of regex, but I am relatively inexperienced with these, and the different URL structures make it all the more tricky. Any help is much appreciated, thanks!
The canonical regex way of doing this is to search on word boundaries:
pry(main)> "foo/womens/bar".match(/\bwomens\b/)
=> #<MatchData "womens">
pry(main)> "foo/womens/bar".match(/\bmens\b/)
=> nil
pry(main)> "foo/mens/bar".match(/\bmens\b/)
=> #<MatchData "mens">
pry(main)> "foo/mens/bar".match(/\bwomens\b/)
=> nil
That said, either splitting, or searching with the leading "/", may be adequate.
If you first check for women it should work:
# assumes str is not nil
def gender(str)
if str.include?("women")
"F"
elsif str.include?("men")
"M"
else
nil
end
end
If this is not what you are looking for, please explain your problem in more detail.
You could split with / and check for string equality on the component(s) you want -- no need for a regex there
keyword = "women"
url = "www.website1.com/shop/womens/tops/tshirt"
/\/#{keyword}/ =~ url
=> 21
keyword = "men"
url = "www.website1.com/shop/womens/tops/tshirt"
/\/#{keyword}/ =~ url
=> nil
keyword = "women"
url = www.website2.com/products/womens-tshirt
/\/#{keyword}/ =~ url
=> 25
keyword = "men"
url = www.website2.com/products/womens-tshirt
/\/#{keyword}/ =~ url
=> nil
Then just do a !! on it:
=> !!nil => false
=> !!25 => true

Resources