Regex express to find a number in a string in ruby - ruby

'xyz_1_yx'
'xyz-1-yx'
I have tried to find the correct regex expression to extract 1
def find_number(image_basename)
startIndex= 1
endIndex= 10
(startIndex..endIndex).each do |n|
return n if /\b-|_#{n}-|_\b/.match(image_basename)
end
nil
end
2.5.0 :376 > find_number('xyz_1_yx')
=> nil

b
=> "asdas-12-asd"
b.scan(/([A-z]*)(_{1}|-{1})(\d*)(_{1}|-{1})([A-z]*)/)
=> [["asdas", "-", "12", "-", "asd"]]
UPDATE
here it is with noncapturing group
b.scan(/(?:[A-z]*)(?:_{1}|-{1})(\d*)(?:_{1}|-{1})(?:[A-z]*)/)
=> [["12"]]

str = 'xyz_1_y1x'
If you want the first '1',
r = /1/
str[r]
#=> "1"
which uses the method String#[]. Alternatively:
str.each_char.find { |c| c == '1' }
#=> `'1'`
If you want all '1''s, use String#scan:
str.scan r
#=> ["1", "1"]
Note that str[r] is equivalent to determining whether the string contains one or more '1''s and str.scan(r) tells us nothing more than the number of '1' that are contained in the string.
If you want to extract any digit, change the regular expression: r = /\d/.

As per my understanding of your question, you want the characters between - or _. If that's the case you can simply do
str = 'xyz_1_yx'
str.split(/[-,_]/)[1]
#=> '1'
str = 'xyz-129-yx'
str.split(/[-,_]/)[1]
#=> '129'

Related

Increment alphabetical string in Ruby on rails

Task I want to solve:
Write a program that takes a string, will perform a transformation and return it.
For each of the letters of the parameter string switch it by the next one in alphabetical order.
'z' becomes 'a' and 'Z' becomes 'A'. Case remains unaffected.
def rotone(param_1)
a = ""
param_1.each_char do |x|
if x.count("a-zA-Z") > 0
a << x.succ
else
a << x
end
end
a
end
And I take this:
Input: "AkjhZ zLKIJz , 23y "
Expected Return Value: "BlkiA aMLJKa , 23z "
Return Value: "BlkiAA aaMLJKaa , 23z "
When iterators find 'z' or 'Z' it increment two times z -> aa or Z -> AA
input = "AkjhZ zLKIJz , 23y"
Code
p input.tr('a-yA-YzZ','b-zB-ZaA')
Output
"BlkiA aMLJKa , 23z"
Your problem is that String#succ (aka String#next) has been designed in a way that does not serve your purpose when the receiver is 'z' or 'Z':
'z'.succ #=> 'aa'
'Z'.succ #=> 'AA'
If you replaced a << x.succ with a << x.succ[0] you would obtain the desired result.
You might consider writing that as follows.
def rotone(param_1)
param_1.gsub(/./m) { |c| c.match?(/[a-z]/i) ? c.succ[0] : c }
end
String#gsub's argument is a regular expression that matches every character (so every character is passed to gsub's block)1.
See also String#match?. The regular expression /[a-z]/i matches every character that is one of the characters in the character class [a-z]. The option i makes the match case-independent, so uppercase letters are matched as well.
Here is alternative way to write the method that employs two hashes that are defined as constants.
CODE = [*'a'..'z', *'A'..'Z'].each_with_object({}) do |c,h|
h[c] = c.succ[0]
end.tap { |h| h.default_proc = proc { |_h,k| k } }
#=> {"a"=>"b", "b"=>"c",..., "y"=>"z", "z"=>"a",
# "A"=>"B", "B"=>"C",..., "Y"=>"Z", "Z"=>"A"}
DECODE = CODE.invert.tap { |h| h.default_proc = proc { |_h,k| k } }
#=> {"b"=>"a", "c"=>"b", ..., "z"=>"y", "a"=>"z",
# "B"=>"A", "C"=>"B", ..., "Z"=>"Y", "A"=>"Z"}
For example,
CODE['e'] #=> "f"
CODE['Z'] #=> "A"
CODE['?'] #=> "?"
DECODE['f'] #=> "e"
DECODE['A'] #=> "Z"
DECODE['?'] #=> "?"
Let's try using gsub, CODE and DECODE with an example string.
str = "The quick brown dog Zelda jumped over the lazy fox Arnie"
rts = str.gsub(/./m, CODE)
#=> "Uif rvjdl cspxo eph Afmeb kvnqfe pwfs uif mbaz gpy Bsojf"
rts.gsub(/./m, DECODE)
#=> "The quick brown dog Zelda jumped over the lazy fox Arnie"
See Hash#merge, Object#tap, Hash#default_proc=, Hash#invert and the form of Sting#gsub that takes a hash as its optional second argument.
Adding the default proc to the hash h causes h[k] to return k if h does not have a key k. Had CODE been defined without the default proc,
CODE = [*'a'..'z', *'A'..'Z'].each_with_object({}) { |c,h| h[c] = c.succ[0] }
#=> {"a"=>"b", "b"=>"c",..., "y"=>"z", "z"=>"a",
# "A"=>"B", "B"=>"C",..., "Y"=>"Z", "Z"=>"A"}
gsub would skip over characters that are not letters:
rts = str.gsub(/./m, CODE)
#=> "UifrvjdlcspxoephAfmebkvnqfepwfsuifmbazgpyBsojf"
Without the default proc we would have to write
rts = str.gsub(/./m) { |s| CODE.fetch(s, s) }
#=> "Uif rvjdl cspxo eph Afmeb kvnqfe pwfs uif mbaz gpy Bsojf"
See Hash#fetch.
1. The regular expression /./ matches every character other than line terminators. Adding the option m (/./m) causes . to match line terminators as well.

Trying to remove punctuation without using regex

I am trying to remove punctuation from an array of words without using regular expression. In below eg,
str = ["He,llo!"]
I want:
result # => ["Hello"]
I tried:
alpha_num="abcdefghijklmnopqrstuvwxyz0123456789"
result= str.map do |punc|
punc.chars {|ch|alpha_num.include?(ch)}
end
p result
But it returns ["He,llo!"] without any change. Can't figure out where the problem is.
include? block returns true/false, try use select function to filter illegal characters.
result = str.map {|txt| txt.chars.select {|c| alpha_num.include?(c.downcase)}}
.map {|chars| chars.join('')}
p result
str=["He,llo!"]
alpha_num="abcdefghijklmnopqrstuvwxyz0123456789"
Program
v=[]<<str.map do |x|
x.chars.map do |c|
alpha_num.chars.map.include?(c.downcase) ? c : nil
end
end.flatten.compact.join
p v
Output
["Hello"]
exclusions = ((32..126).map(&:chr) - [*'a'..'z', *'A'..'Z', *'0'..'9']).join
#=> " !\"\#$%&'()*+,-./:;<=>?#[\\]^_`{|}~"
arr = ['He,llo!', 'What Ho!']
arr.map { |word| word.delete(exclusions) }
#=> ["Hello", "WhatHo"]
If you could use a regular expression and truly only wanted to remove punctuation, you could write the following.
arr.map { |word| word.gsub(/[[:punct:]]/, '') }
#=> ["Hello", "WhatHo"]
See String#delete. Note that arr is not modified.

How to mask all but last four characters in a string

I've been attempting a coding exercise to mask all but the last four digits or characters of any input.
I think my solution works but it seems a bit clumsy. Does anyone have ideas about how to refactor it?
Here's my code:
def mask(string)
z = string.to_s.length
if z <= 4
return string
elsif z > 4
array = []
string1 = string.to_s.chars
string1[0..((z-1)-4)].each do |s|
array << "#"
end
array << string1[(z-4)..(z-1)]
puts array.join(", ").delete(", ").inspect
end
end
positive lookahead
A positive lookahead makes it pretty easy. If any character is followed by at least 4 characters, it gets replaced :
"654321".gsub(/.(?=.{4})/,'#')
# "##4321"
Here's a description of the regex :
r = /
. # Just one character
(?= # which must be followed by
.{4} # 4 characters
) #
/x # free-spacing mode, allows comments inside regex
Note that the regex only matches one character at a time, even though it needs to check up to 5 characters for each match :
"654321".scan(r)
# => ["6", "5"]
/(.)..../ wouldn't work, because it would consume 5 characters for each iteration :
"654321".scan(/(.)..../)
# => [["6"]]
"abcdefghij".scan(/(.)..../)
# => [["a"], ["f"]]
If you want to parametrize the length of the unmasked string, you can use variable interpolation :
all_but = 4
/.(?=.{#{all_but}})/
# => /.(?=.{4})/
Code
Packing it into a method, it becomes :
def mask(string, all_but = 4, char = '#')
string.gsub(/.(?=.{#{all_but}})/, char)
end
p mask('testabcdef')
# '######cdef'
p mask('1234')
# '1234'
p mask('123')
# '123'
p mask('x')
# 'x'
You could also adapt it for sentences :
def mask(string, all_but = 4, char = '#')
string.gsub(/\w(?=\w{#{all_but}})/, char)
end
p mask('It even works for multiple words')
# "It even #orks for ####iple #ords"
Some notes about your code
string.to_s
Naming things is very important in programming, especially in dynamic languages.
string.to_s
If string is indeed a string, there shouldn't be any reason to call to_s.
If string isn't a string, you should indeed call to_s before gsub but should also rename string to a better description :
object.to_s
array.to_s
whatever.to_s
join
puts array.join(", ").delete(", ").inspect
What do you want to do exactly? You could probably just use join :
[1,2,[3,4]].join(", ").delete(", ")
# "1234"
[1,2,[3,4]].join
# "1234"
delete
Note that .delete(", ") deletes every comma and every whitespace, in any order. It doesn't only delete ", " substrings :
",a b,,, cc".delete(', ')
# "abcc"
["1,2", "3,4"].join(', ').delete(', ')
# "1234"
Ruby makes this sort of thing pretty trivial:
class String
def asteriskify(tail = 4, char = '#')
if (length <= tail)
self
else
char * (length - tail) + self[-tail, tail]
end
end
end
Then you can apply it like this:
"moo".asteriskify
# => "moo"
"testing".asteriskify
# => "###ting"
"password".asteriskify(5, '*')
# => "***sword"
Try this one
def mask(string)
string[0..-5] = '#' * (string.length - 4)
string
end
mask("12345678")
=> "####5678"
I will add my solution to this topic too :)
def mask(str)
str.match(/(.*)(.{4})/)
'#' * ($1 || '').size + ($2 || str)
end
mask('abcdef') # => "##cdef"
mask('x') # => "x"
I offer this solution mainly to remind readers that String#gsub without a block returns an enumerator.
def mask(str, nbr_unmasked, mask_char)
str.gsub(/./).with_index { |s,i| i < str.size-nbr_unmasked ? mask_char : s }
end
mask("abcdef", 4, '#')
#=> "##cdef"
mask("abcdef", 99, '#')
#=> "######"
Try using tap
def mask_string(str)
str.tap { |p| p[0...-4] = '#' * (p[0...-4].length) } if str.length > 4
str
end
mask_string('ABCDEF') # => ##CDEF
mask_string('AA') # => AA
mask_string('S') # => 'S'

Check whether a string contains all the characters of another string in Ruby

Let's say I have a string, like string= "aasmflathesorcerersnstonedksaottersapldrrysaahf". If you haven't noticed, you can find the phrase "harry potter and the sorcerers stone" in there (minus the space).
I need to check whether string contains all the elements of the string.
string.include? ("sorcerer") #=> true
string.include? ("harrypotterandtheasorcerersstone") #=> false, even though it contains all the letters to spell harrypotterandthesorcerersstone
Include does not work on shuffled string.
How can I check if a string contains all the elements of another string?
Sets and array intersection don't account for repeated chars, but a histogram / frequency counter does:
require 'facets'
s1 = "aasmflathesorcerersnstonedksaottersapldrrysaahf"
s2 = "harrypotterandtheasorcerersstone"
freq1 = s1.chars.frequency
freq2 = s2.chars.frequency
freq2.all? { |char2, count2| freq1[char2] >= count2 }
#=> true
Write your own Array#frequency if you don't want to the facets dependency.
class Array
def frequency
Hash.new(0).tap { |counts| each { |v| counts[v] += 1 } }
end
end
I presume that if the string to be checked is "sorcerer", string must include, for example, three "r"'s. If so you could use the method Array#difference, which I've proposed be added to the Ruby core.
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
str = "aasmflathesorcerersnstonedksaottersapldrrysaahf"
target = "sorcerer"
target.chars.difference(str.chars).empty?
#=> true
target = "harrypotterandtheasorcerersstone"
target.chars.difference(str.chars).empty?
#=> true
If the characters of target must not only be in str, but must be in the same order, we could write:
target = "sorcerer"
r = Regexp.new "#{ target.chars.join "\.*" }"
#=> /s.*o.*r.*c.*e.*r.*e.*r/
str =~ r
#=> 2 (truthy)
(or !!(str =~ r) #=> true)
target = "harrypotterandtheasorcerersstone"
r = Regexp.new "#{ target.chars.join "\.*" }"
#=> /h.*a.*r.*r.*y* ... o.*n.*e/
str =~ r
#=> nil
A different albeit not necessarily better solution using sorted character arrays and sub-strings:
Given your two strings...
subject = "aasmflathesorcerersnstonedksaottersapldrrysaahf"
search = "harrypotterandthesorcerersstone"
You can sort your subject string using .chars.sort.join...
subject = subject.chars.sort.join # => "aaaaaaacddeeeeeffhhkllmnnoooprrrrrrssssssstttty"
And then produce a list of substrings to search for:
search = search.chars.group_by(&:itself).values.map(&:join)
# => ["hh", "aa", "rrrrrr", "y", "p", "ooo", "tttt", "eeeee", "nn", "d", "sss", "c"]
You could alternatively produce the same set of substrings using this method
search = search.chars.sort.join.scan(/((.)\2*)/).map(&:first)
And then simply check whether every search sub-string appears within the sorted subject string:
search.all? { |c| subject[c] }
Create a 2 dimensional array out of your string letter bank, to associate the count of letters to each letter.
Create a 2 dimensional array out of the harry potter string in the same way.
Loop through both and do comparisons.
I have no experience in Ruby but this is how I would start to tackle it in the language I know most, which is Java.

Outputs string if string contains vowels and is in alphabetical order

I'm working on the following exercise below:
Write a method, ovd(str) that takes a string of lowercase words and returns a string with just the words containing all their vowels (excluding "y") in alphabetical order. Vowels may be repeated ("afoot" is an ordered vowel word). The method does not return the word if it is not in alphabetical order.
Example output is:
ovd("this is a test of the vowel ordering system") #output=> "this is a test of the system"
ovd("complicated") #output=> ""
Below is code I wrote that will do the job but I am looking to see if there is a shorter more clever way to do this. My solution seems too lengthy.Thanks in advance for helping.
def ovd?(str)
u=[]
k=str.split("")
v=["a","e","i","o","u"]
w=k.each_index.select{|i| v.include? k[i]}
r={}
for i in 0..v.length-1
r[v[i]]=i+1
end
w.each do |s|
u<<r[k[s]]
end
if u.sort==u
true
else
false
end
end
def ovd(phrase)
l=[]
b=phrase.split(" ")
b.each do |d|
if ovd?(d)==true
l<<d
end
end
p l.join(" ")
end
def ovd(str)
str.split.select { |word| "aeiou".match(/#{word.gsub(/[^aeiou]/, "").chars.uniq.join(".*")}/) }.join(" ")
end
ovd("this is a test of the vowel ordering system") # => "this is a test of the system"
ovd("complicated") # => ""
ovd("alooft") # => "alooft"
ovd("this is testing words having something in them") # => "this is testing words having in them"
EDIT
As requested by the OP, explanation
String#gsub word.gsub(/[^aeiou]/, "") removes the non-vowel characters e.g
"afloot".gsub(/[^aeiou]/, "") # => "aoo"
String#chars converts the new word to an array of characters
"afloot".gsub(/[^aeiou]/, "").chars # => ["a", "o", "o"]
Array#uniq converts returns only unique elements from the array e.g
"afloot".gsub(/[^aeiou]/, "").chars.uniq # => ["a", "o"]
Array#join converts an array to a string merging it with the supplied parameter e.g
"afloot".gsub(/[^aeiou]/, "").chars.uniq.join(".*") # => "a.*o"
#{} is simply String interpolation and // converts the interpolated string into a Regular Expression
/#{"afloot".gsub(/[^aeiou]/, "").chars.uniq.join(".*")}/ # => /a.*o/
A non-regex solution:
V = %w[a e i o u] # => ["a", "e", "i", "o", "u"]
def ovd(str)
str.split.select{|w| (x = w.downcase.chars.select \
{|c| V.include?(c)}) == x.sort}.join(' ')
end
ovd("this is a test of the vowel ordering system")
# => "this is a test of the system"
ovd("") # => ""
ovd("camper") # => "camper"
ovd("Try singleton") # => "Try"
ovd("I like leisure time") # => "I"
ovd("The one and only answer is '42'") # => "The and only answer is '42'"
ovd("Oil and water don't mix") # => "and water don't mix"
Edit to add an alternative:
NV = (0..127).map(&:chr) - %w(a e i o u) # All ASCII chars except lc vowels
def ovd(str)
str.split.select{|w| (x = w.downcase.chars - NV) == x.sort}.join(' ')
end
Note x = w.downcase.chars & V does not work. While it spears out the vowels from w, and preserves their order, it removes duplicates.

Resources