I want to return true if the user enters only one of a set of possible matches. Similar to an XOR operator, but only one string out of the entire group may exist in the input. Here is my code:
if input.match?(/str|con|dex|wis|int|cha/)
The following input
s should return true:
+2 int
-3con
str
con
wisdom
dexterity
The following input
s should return false:
+1 int +2 cha
-4dex+3con-1cha
int cha
str dex
con wis cha
strength intelligence
strdex
I'd probably go with String#scan
and a simple regex so that you can understand what you've done later:
if input.scan(/str|dex|con|int|wis|cha/).length == 1
# Found exactly one
else
# Didn't find it or found too many
end
That also makes it easier to distinguish between the various ways it can fail.
Presumably your strings will be relatively small so scanning the string for all the matches won't have any noticeable overhead.
The following are three ways to answer the question without creating an intermediate array. All employ the regular expression:
R = /str|con|dex|wis|int|cha/
and return the following:
one_match? "It wasn't a con, really" #=> true
one_match? "That sounds to me like a wild guess." #=> falsy (nil or false)
one_match? "Both int and dex are present." #=> falsy (nil or false)
one_match? "Three is an integer." #=> true
one_match? "Both int and indexes are present." #=> falsy (nil or false)
#1 Do first and last match begin at the same index?
def one_match?(s)
(idx = s.index(R)) && idx == s.rindex(R)
end
See String#index and String#rindex.
#2 Use the form of String#index
that takes an argument equal to the index at which the search is to begin.
def one_match?(s)
s.index(R) && s.index(R, Regexp.last_match.end(0)).nil?
end
See Regexp::last_match and MatchData#end. Regexp.last_match
can be replaced by $~
.
#3 Use the form of String#gsub that takes one argument and no block to create an enumerator that generates matches
def one_match?(s)
s.gsub(/str|con|dex|wis|int|cha/).count { true } == 1
end
See Enumerable#count.
Alternatively,
s.gsub(/str|con|dex|wis|int|cha/).to_a.size == 1
though this has the disadvantage of creating a temporary array.
To match whole words only
In the penultimate example 'int'
matches 'int'
in 'integer'
and in the last match 'dex'
matches 'dex'
in 'indexes'
. To enforce full-word matches the regular expression can be changed to:
/\b(?:str|con|dex|wis|int|cha)\b/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With