This may be a really simple regex but its one of those problems that have proven hard to google.
I have error codes coming back from a third party system. They are supposed to be in the format:
ZZZ##
where Z is Alpha and # is numeric. They are supposed to be 0 padded, but i'm finding that sometimes they come back
ZZZ#
without the 0 padding.
Anyone know how i could add the 0 padding so i can use the string as an index to a hash?
Here's my take:
def pad str
number = str.scan(/\d+/).first
str[number] = "%02d" % number.to_i
str
end
6.times do |n|
puts pad "ZZZ#{7 + n}"
end
# >> ZZZ07
# >> ZZZ08
# >> ZZZ09
# >> ZZZ10
# >> ZZZ11
# >> ZZZ12
Reading:
String#[]=
Kernel#sprintf and formatting flags.
fixed = str.gsub /([a-z]{3})(\d)(?=\D|\z)/i, '\10\2'
That says:
Find three letters
…followed by a digit
…and make sure that then you see either a non-digit or the end of file
and replace with the three letters (\1), a zero (0), and then the digit (\2)
To pad to an arbitrary length, you could:
# Pad to six digits
fixed = str.gsub /([a-z]{3})(\d+)/i do
"%s%06d" % [ $1, $2.to_i ]
end
Here's mine:
"ZZZ7".gsub(/\d+/){|x| "%02d" % x}
=> "ZZZ07"
There's probably a million ways to do this but here's another look.
str.gsub!(/[0-9]+/ , '0\0' ) if str.length < 5
Related
How to check that there are at least two the same values on four-digit number or there aren't at least two the same values
for example 2432 - there are two 2, how to check it and then spit out an information that there are at least two or more the same numbers in this four-digit number?
puts "enter four-digit number: "
four_digit_num = gets.chomp.to_i
You can do that as follows.
r = /(\d)\d*\1/
gets.match?(r) # when gets #=> "1231\n"
#=> true
gets.match?(r) # when gets #=> "1232\n"
#=> true
gets.match?(r) # when gets #=> "1234\n"
#=> false
We can write the regular expression in free-spacing mode to make it self-documenting.
r = /
(\d) # match a digit and save to capture group 1
\d* # match zero or more digits
\1 # match the contents of capture group 1
/x # specify free-spacing regex definition mode
See String#match?.
If you must begin with the integer
four_digit_num = gets.to_i
you could write
arr = four_digit_num.digits
arr.uniq.size < arr.size
or convert it to a string and apply the first method above:
four_digit_num.to_s.match?(r)
Test for unique digits:
four_digit_num = gets.to_i
digits = four_digit_num.digits # leading zeros will disappear
puts "2 or more same digits." if digits != digits.uniq
You can have up to two pairs of duplicates, so that's probably a use case that needs to be handled. In any case, with Ruby 2.7.2 you might use a Hash to count occurrences of each digit in your String using Hash#tally (via Enumerable) like so:
def report_duplicates_in(digits)
dupes = digits.to_s.chars.tally.select { _2 > 1 }
puts "dupliicate digits=>counts for #{digits}: #{dupes}" if dupes.any?
[digits, dupes]
end
# test with some values
%w[1234 2432 2442].map do |four_digit_num|
report_duplicates_in(four_digit_num)
end.to_h
This will print the following to standard output and return a Hash:
dupliicate digits=>counts for 2432: {"2"=>2}
dupliicate digits=>counts for 2442: {"2"=>2, "4"=>2}
#=> {"1234"=>{}, "2432"=>{"2"=>2}, "2442"=>{"2"=>2, "4"=>2}}
I have regex as follows:
/^(\d|-|\(|\)|\+|\s){12,}$/
This will allow digits, (, ), space. But I want to ensure string contains atleast 8 digits.
Some allowed strings are as follows:
(1323 ++24)233
24243434 43
++++43435++4554345 434
It should not allow strings like:
((((((1213)))
++++232+++
Use Look ahead within your regex at the start..
/^(?=(.*\d){8,})[\d\(\)\s+-]{8,}$/
---------------
|
|->this would check for 8 or more digits
(?=(.*\d){8,}) is zero width look ahead that checks for 0 to many character (i.e .*) followed by a digit (i.e \d) 8 to many times (i.e.{8,0})
(?=) is called zero width because it doesnt consume the characters..it just checks
To restict it to 14 digits you can do
/^(?=([^\d]*\d){8,14}[^\d]*$)[\d\(\)\s+-]{8,}$/
try it here
Here's a non regular expression solution
numbers = ["(1323 ++24)233", "24243434 43" , "++++43435++4554345 434", "123 456_7"]
numbers.each do |number|
count = 0
number.each_char do |char|
count += 1 if char.to_i.to_s == char
break if count > 7
end
puts "#{count > 7}"
end
No need to mention ^, $, or the "or more" part of {8,}, or {12,}, which is unclear where it comes from.
The following makes the intention transparent.
r = /
(?=(?:.*\d){8}) # First condition: Eight digits
(?!.*[^-\d()+\s]) # Second condition: Characters other than `[-\d()+\s]` should not be included.
/x
resulting in:
"(1323 ++24)233" =~ r #=> 0
"24243434 43" =~ r #=> 0
"++++43435++4554345 434" =~ r #=> 0
"((((((1213)))" =~ r #=> nil
"++++232+++" =~ r #=> nil
What is the preferred way of removing the last n characters from a string?
irb> 'now is the time'[0...-4]
=> "now is the "
If the characters you want to remove are always the same characters, then consider chomp:
'abc123'.chomp('123') # => "abc"
The advantages of chomp are: no counting, and the code more clearly communicates what it is doing.
With no arguments, chomp removes the DOS or Unix line ending, if either is present:
"abc\n".chomp # => "abc"
"abc\r\n".chomp # => "abc"
From the comments, there was a question of the speed of using #chomp versus using a range. Here is a benchmark comparing the two:
require 'benchmark'
S = 'asdfghjkl'
SL = S.length
T = 10_000
A = 1_000.times.map { |n| "#{n}#{S}" }
GC.disable
Benchmark.bmbm do |x|
x.report('chomp') { T.times { A.each { |s| s.chomp(S) } } }
x.report('range') { T.times { A.each { |s| s[0...-SL] } } }
end
Benchmark Results (using CRuby 2.13p242):
Rehearsal -----------------------------------------
chomp 1.540000 0.040000 1.580000 ( 1.587908)
range 1.810000 0.200000 2.010000 ( 2.011846)
-------------------------------- total: 3.590000sec
user system total real
chomp 1.550000 0.070000 1.620000 ( 1.610362)
range 1.970000 0.170000 2.140000 ( 2.146682)
So chomp is faster than using a range, by ~22%.
Ruby 2.5+
As of Ruby 2.5 you can use delete_suffix or delete_suffix! to achieve this in a fast and readable manner.
The docs on the methods are here.
If you know what the suffix is, this is idiomatic (and I'd argue, even more readable than other answers here):
'abc123'.delete_suffix('123') # => "abc"
'abc123'.delete_suffix!('123') # => "abc"
It's even significantly faster (almost 40% with the bang method) than the top answer. Here's the result of the same benchmark:
user system total real
chomp 0.949823 0.001025 0.950848 ( 0.951941)
range 1.874237 0.001472 1.875709 ( 1.876820)
delete_suffix 0.721699 0.000945 0.722644 ( 0.723410)
delete_suffix! 0.650042 0.000714 0.650756 ( 0.651332)
I hope this is useful - note the method doesn't currently accept a regex so if you don't know the suffix it's not viable for the time being. However, as the accepted answer (update: at the time of writing) dictates the same, I thought this might be useful to some people.
str = str[0..-1-n]
Unlike the [0...-n], this handles the case of n=0.
I would suggest chop. I think it has been mentioned in one of the comments but without links or explanations so here's why I think it's better:
It simply removes the last character from a string and you don't have to specify any values for that to happen.
If you need to remove more than one character then chomp is your best bet. This is what the ruby docs have to say about chop:
Returns a new String with the last character removed. If the string
ends with \r\n, both characters are removed. Applying chop to an empty
string returns an empty string. String#chomp is often a safer
alternative, as it leaves the string unchanged if it doesn’t end in a
record separator.
Although this is used mostly to remove separators such as \r\n I've used it to remove the last character from a simple string, for example the s to make the word singular.
name = "my text"
x.times do name.chop! end
Here in the console:
>name = "Nabucodonosor"
=> "Nabucodonosor"
> 7.times do name.chop! end
=> 7
> name
=> "Nabuco"
Dropping the last n characters is the same as keeping the first length - n characters.
Active Support includes String#first and String#last methods which provide a convenient way to keep or drop the first/last n characters:
require 'active_support/core_ext/string/access'
"foobarbaz".first(3) # => "foo"
"foobarbaz".first(-3) # => "foobar"
"foobarbaz".last(3) # => "baz"
"foobarbaz".last(-3) # => "barbaz"
if you are using rails, try:
"my_string".last(2) # => "ng"
[EDITED]
To get the string WITHOUT the last 2 chars:
n = "my_string".size
"my_string"[0..n-3] # => "my_stri"
Note: the last string char is at n-1. So, to remove the last 2, we use n-3.
Check out the slice() method:
http://ruby-doc.org/core-2.5.0/String.html#method-i-slice
You can always use something like
"string".sub!(/.{X}$/,'')
Where X is the number of characters to remove.
Or with assigning/using the result:
myvar = "string"[0..-X]
where X is the number of characters plus one to remove.
If you're ok with creating class methods and want the characters you chop off, try this:
class String
def chop_multiple(amount)
amount.times.inject([self, '']){ |(s, r)| [s.chop, r.prepend(s[-1])] }
end
end
hello, world = "hello world".chop_multiple 5
hello #=> 'hello '
world #=> 'world'
Using regex:
str = 'string'
n = 2 #to remove last n characters
str[/\A.{#{str.size-n}}/] #=> "stri"
x = "my_test"
last_char = x.split('').last
In Ruby language, how can I get the number of lines in a string?
There is a lines method for strings which returns an Enumerator. Call count on the enumerator.
str = "Hello\nWorld"
str.lines.count # 2
str = "Hello\nWorld\n" # trailing newline is ignored
str.lines.count # 2
The lines method was introduced in Ruby 1.8.7. If you're using an older version, checkout the answers by #mipadi and #Greg.
One way would be to count the number of line endings (\n or \r\n, depending on the string), the caveat being that if the string does not end in a new line, you'll have to make sure to add one to your count. You could do so with the following:
c = my_string.count("\n")
c += 1 unless c[-1,1] == "\n"
You could also just loop through the string and count the lines:
c = 0
my_string.each { |line| c += 1 }
Continuing with that solution, you could get really fancy and use inject:
c = my_string.each.inject(0) { |count, line| count += 1 }
string".split("\n").size works nicely. I like that it ignores trailing new-lines if they don't contain content.
"Hello\nWorld\n".split("\n") # => ["Hello", "World"]
"hello\nworld\nfoo bar\n\n".split("\n").size # => 3
That might not be what you want, so use lines() as #Anurag suggested instead if you need to honor all new-lines.
"hello\nworld\nfoo bar\n\n".lines.count # => 4
"hello\nworld\nfoo bar\n\n".chomp.split("\n",-1).size # => 4
String#chomp gets rid of an end of line if it exists, and the -1 allows empty strings.
given a file object (here, in rails)
file = File.open(File.join(Rails.root, 'lib', 'file.json'))
file.readlines.count
returns the number of lines
IO#readlines performs a split method on strings (IOStrings in this case) using newlines as the separator
This will not count blank lines:
string.split("\n").select{ |line| line != "" }.size
One\n
Two\n
Three\n
Four\n
remove_lines(2) would remove the first two lines, leaving the string:
Three\n
Four\n
s.to_a[2..-1].join
>> s = "One\nTwo\nThree\nFour\n"
=> "One\nTwo\nThree\nFour\n"
>> s.to_a[2..-1].join
=> "Three\nFour\n"
s = "One\nTwo\nThree\nFour"
lines = s.lines
> ["One\n", "Two\n", "Three\n", "Four"]
remaining_lines = lines[2..-1]
> ["Three\n", "Four"]
remaining_lines.join
> "Three\nFour"
String#lines converts the string into an array of lines (retaining the new line character at the end of each string)
[2..-1] specifies the range of lines to return, in this case the third through the last
Array#join concatenates the lines back together, without any space (but since the lines still contain the new line character, we don't need a separator)
In one line:
s.lines[2..-1].join
class String
def remove_lines(i)
split("\n")[i..-1].join("\n")
end
end
Calling "One\nTwo\nThree\nFour\n".remove_lines(2) would result in "Three\nFour". If you need the trailing "\n" you need to extend this method accordingly.
I had a situation where I needed to support multiple platform EOLN (both \r and \n), and had success with the following:
split(/\r\n|\r|\n/, 2).last
Or the equivalent remove_lines:
def remove_lines(number_of_lines=1)
split(/\r\n|\r|\n/, number_of_lines+1).last
end
Here is a pure regexp one-liner. Hypothetically it should be even faster than the elegant solution provided by #DigitalRoss:
n = 4 # number of lines
str.gsub(/\A(.*\n){#{n}}/,'')
If you know in advance how many line you want to cut (4 here):
str.gsub(/\A(.*\n){4}/,'')
And if you want to cut only one line:
str.gsub(/\A.*\n/,'')
In order to cut n lines from the tail:
gsub(/(\n.*){#{n}}\Z/,'')
This problem will remove the first two lines using regular expression.
Text = "One\nTwo\nThree\nFour"
Text = Text.gsub /^(?:[^\n]*\n){2}/, ''
# -----------------------------------^^ (2) Replace with nothing
# ----------------^^^^^^^^^^^^^^^^ (1) Detect first 2 lines
puts Text
EDIT: I've just saw that the question is also about 'n' lines not just two lines.
So here is my new answer.
Lines_Removed = 2
Original_Text = "One\nTwo\nThree\nFour"
Result___Text = (Original_Text.gsub(Regexp.new("([^\n]*\n){%s}" % Lines_Removed), ''))
# ^^^^^^^^^^^^^^ ^^
# - (1) Detect first lines -----++++++++++++++ ||
# - (2) Replace with nothing -----------------------------------------------------++
puts Result___Text # Returns "Three\nFour"
def remove_lines(str, n)
res = ""
arr = str.split("\n")[n..(str.size-n)]
arr.each { |i| res.concat(i + "\n") }
return res
end
a = "1\n2\n3\n4\n"
b = remove_lines(a, 2)
print b