How can I test whether a Ruby string contains only a specific set of characters?
For example, if my set of allowed characters is "AGHTM" plus digits 0-9,
- the string - "MT3G22AH"is valid;
- the string - "TAR34"is invalid (because of the- R).
How can I test whether a Ruby string contains only a specific set of characters?
For example, if my set of allowed characters is "AGHTM" plus digits 0-9,
the string "MT3G22AH" is valid;
the string "TAR34" is invalid (because of the R).
 
    
     
    
    A nicely idiomatic non-regex solution is to use String#count:
"MT3G22AH".count("^AGHTM0-9").zero?  # => true
"TAR34".count("^AGHTM0-9").zero?     # => false
The inverse also works, if you find it more readable:
"MT3G22AH".count('AGHTM0-9') == "MT3G22AH".size  # => true
Take your pick.
For longer strings, both methods here perform significantly better than regex-based options.
 
    
    allowed = "AGHTM"
allowed = /\A[\d#{allowed}]+\z/i
"MT3G22AH" =~ allowed #⇒ truthy
"TAR34" =~ allowed #⇒ falsey
 
    
    One possibility is to delete all the allowed characters and check if the resulting string is empty :
"MT3G22AH".delete("AGHTM0-9").empty?
#=> true
"TAR34".delete("AGHTM0-9").empty?
#=> false
For short strings, @steenslag is the fastest method, followed by @Jesse and my method.
def mudasobwa(string)
  allowed = 'AGHTM'
  allowed = /\A[\d#{allowed}]+\z/i
  string.match? allowed
end
def eric(string)
  string.delete('AGHTM1-9').empty?
end
def meagar(string)
  allowed = 'AGHTM0123456789'
  string.chars.uniq.all? { |c| allowed.include?(c) }
end
def jesse(string)
  string.count('^AGHTM0-9').zero?
end
def steenslag(string)
  !string.match?(/[^AGHTM0-9]/) 
end
require 'fruity'
n = 1
str1 = 'MT3G22AH' * n
str2 = 'TAR34' * n
compare do
  _jesse { [jesse(str1), jesse(str2)] }
  _eric { [eric(str1), eric(str2)] }
  _mudasobwa { [mudasobwa(str1), mudasobwa(str2)] }
  _meagar { [meagar(str1), meagar(str2)] }
  _steenslag { [steenslag(str1), steenslag(str2)] }
end
It outputs :
Running each test 1024 times. Test will take about 2 seconds.
_steenslag is faster than _jesse by 2.2x ± 0.1
_jesse is faster than _eric by 8.000000000000007% ± 1.0%
_eric is faster than _meagar by 4.3x ± 0.1
_meagar is faster than _mudasobwa by 2.4x ± 0.1
For longer strings ( n=5000), @Jesse becomes the fastest method.
Running each test 32 times. Test will take about 12 seconds.
_jesse is faster than _eric by 2.5x ± 0.01
_eric is faster than _mudasobwa by 4x ± 1.0
_mudasobwa is faster than _steenslag by 2x ± 0.1
_steenslag is faster than _meagar by 11x ± 0.1
 
    
    This seems to be faster than all previous benchmarks (by @Eric Duminil)(ruby 2.4):
!string.match?(/[^AGHTM0-9]/) 
