ToddT
ToddT

Reputation: 3258

More efficient way than gsub twice

I have an array of strings:

orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]

I am doing this to remove the leading "#" and trailing ".1"

orders.each do |numbers|
  puts numbers.gsub!("#", "").gsub!(/\.[0-9]/, "")
end
# returns 1174, 1176 etc..

The trailing ".1" could be any number to 9.. is there a better/faster way to do this?

Upvotes: 0

Views: 547

Answers (7)

Sagar Pandya
Sagar Pandya

Reputation: 9497

One way to do this:

orders.map { |s| s.sub('#','').to_i }
#=> [1174, 1176, 1177, 1178, 1180] 

add to_s in the block if you want strings.


In case the OP wants the numbers rounded. e.g. #1174.8 returns 1175 etc. Then this should do the trick:

orders = ["#1174.2", "#1176.5", "#1177.2", "#1178.7", "#1180.1"]
#=> ["#1174.2", "#1176.5", "#1177.2", "#1178.7", "#1180.1"]     

orders.map { |s| s.sub('#','').to_f.round.to_s }
#=> ["1174", "1177", "1177", "1179", "1180"]

Upvotes: 4

spickermann
spickermann

Reputation: 106972

It looks like all the numbers have four digits. If that is always true you might want to try this:

orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]
orders.map { |n| n[1..-3] }
#=> ["1174","1176","1177","1178","1180"] 

This obviously works only if all numbers have the same format and length, but it is way faster than using a regexp.

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110725

orders.map { |s| s[1..-1].to_i.to_s }
  #=> ["1174", "1176", "1177", "1178", "1180"]

Remove .to_s if you want an array of integers rather than an array of strings.

Upvotes: 1

the Tin Man
the Tin Man

Reputation: 160571

I'd use:

orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]
orders.map{ |n| n[/\d+/] } # => ["1174", "1176", "1177", "1178", "1180"]

/\d+/ will return the first group of digits found, which means that '#' and .n will be ignored automatically.

Here's a benchmark:

require 'fruity'

orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]

compare do
 ttm { orders.map{ |n| n[/\d+/] } }
 ursus { orders.map { |item| item.gsub(/#(\d+)(\.\d)?/, '\1') } }
 dinesh { orders.join.gsub(/\.[0-9]#/, "#").gsub(/\.[0-9]/, "").split("#") - [""] }
 sagarpandya82 { orders.map { |s| s.sub('#','').to_i.to_s } }
 infused { orders.map { |numbers| numbers.gsub(/(^#|\.\d$)/, '') } }
end

# >> Running each test 1024 times. Test will take about 1 second.
# >> ttm is faster than sagarpandya82 by 60.00000000000001% ± 10.0%
# >> sagarpandya82 is faster than dinesh by 2.0x ± 0.1
# >> dinesh is faster than infused by 39.99999999999999% ± 10.0%
# >> infused is faster than ursus by 10.000000000000009% ± 10.0%

Upvotes: 3

dnsh
dnsh

Reputation: 3633

Use this

orders.join.gsub(/\.[0-9]#/, "#").gsub(/\.[0-9]/, "").split("#") - [""]

Returns

=> ["1174", "1176", "1177", "1178", "1180"]

BreakDown

This method takes 5 steps. This is irrespective of how much elements you have in array.

2.2.5 :019 > orders.join
 => "#1174.2#1176.3#1177.2#1178.1#1180.1" 
2.2.5 :020 > orders.join.gsub(/\.[0-9]#/, "#")
 => "#1174#1176#1177#1178#1180.1" 
2.2.5 :021 > orders.join.gsub(/\.[0-9]#/, "#").gsub(/\.[0-9]/, "")
 => "#1174#1176#1177#1178#1180" 
2.2.5 :022 > orders.join.gsub(/\.[0-9]#/, "#").gsub(/\.[0-9]/, "").split("#")
 => ["", "1174", "1176", "1177", "1178", "1180"] 
2.2.5 :023 > orders.join.gsub(/\.[0-9]#/, "#").gsub(/\.[0-9]/, "").split("#") - [""]
 => ["1174", "1176", "1177", "1178", "1180"] 

Upvotes: -2

infused
infused

Reputation: 24337

cleaned_orders = orders.map { |numbers| numbers.gsub(/(^#|\.\d$)/, '') }

cleaned_orders now contains: ['1174', '1176', '1177', '1178', '1180']

(^#|\.\d$) matches # if it is at the beginning of the string or a single period followed by a single digit at the end of the string.

Upvotes: 2

Ursus
Ursus

Reputation: 30071

Try something like this

orders.map { |item| item.gsub(/#(\d+)(\.\d)?/, '\1') }
 => ["1174", "1176", "1177", "1178", "1180"] 

This works even if some elements doesn't have a dot something end.

Upvotes: 0

Related Questions