How to detect if input string has only one number in Ruby - ruby

As I wrote in the title, I want to find if a string input consists of only one number.
Examples are
222 # true
33333 # true
22334 # false
556677 # false
I thought to use String#reverse but it fails with 556677.
if input == input.reverse
# do something
end
What is the best way?

Non-regex solution:
input.chars.uniq.size == 1
"222".chars.uniq.size == 1 #=> true
"556677".chars.uniq.size == 1 #=> false

You could use a regex like...
^(\d)\1*$
For example, this code...
strs = ['11211', '1', '111', '2222', '212']
strs.each {|str|
puts /^(\d)\1*$/.match(str)
}
...produces...
1
111
2222
Fiddle.

You could use String#squeeze followed by String#[] with a regex that matches a string consisting of a single digit:
"2222".squeeze[/^\d$/] #=> 2
"5566".squeeze[/^\d$/] #=> nil
"333a".squeeze[/^\d$/] #=> nil
"aaaa".squeeze[/^\d$/] #=> nil
The return value is either nil (falsy) or the unique digit (truey).
This is just a fun answer. In real life, I'd do it the way #alex has.

Related

Take in string, return true if after "a", a "z" appears within three places

# Write a method that takes a string in and returns true if the letter
# "z" appears within three letters **after** an "a". You may assume
# that the string contains only lowercase letters.
I came up with this, which seems logical, but for some reason if "z" comes directly after "a", it returns false. Can someone explain why?
def nearby_az(string)
i = 0
if string[i] == "a" && string[i+1] == "z"
return true
elsif string[i] == "a" && string[i+2] == "z"
return true
elsif string[i] == "a" && string[i+3] == "z"
return true
else return false
end
i += 1
end
#shivram has given the reason for your problem. Here are a couple of ways to do it.
Problem is tailor-made for a regular expression
r = /
a # match "a"
.{,2} # match any n characters where 0 <= n <= 2
z # match "z"
/x # extended/free-spacing regex definition mode
!!("wwwaeezdddddd" =~ r) #=> true
!!("wwwaeeezdddddd" =~ r) #=> false
You would normally see this regular expression written
/a.{0,2}z/
but extended mode allows you to document each of its elements. That's not important here but is useful when the regex is complex.
The Ruby trick !!
!! is used to convert truthy values (all but false and nil) to true and falsy values (false or nil) to false:
!!("wwwaeezdddddd" =~ r)
#=> !(!("wwwaeezdddddd" =~ r))
#=> !(!3)
#=> !false
#=> true
!!("wwwaeezdddddd" =~ r)
#=> !(!("wwwaeeezdddddd" =~ r))
#=> !(!nil)
#=> !true
#=> false
but !! is not really necessary, since
puts "hi" if 3 #=> "hi"
puts "hi" if nil #=>
Some don't like !!, arguing that
<condition> ? true : false
is more clear.
A non-regex solution
def z_within_4_of_a?(str)
(str.size-3).times.find { |i| str[i]=="a" && str[i+1,3].include?("z") } ? true : false
end
z_within_4_of_a?("wwwaeezdddddd")
#=> true
z_within_4_of_a?("wwwaeeezdddddd")
#=> false
This uses the methods Fixnum#times, Enumerable#find and String#include? (and String#size of course).
Your solution is incorrect. You are considering only the case where String starts with a (with i = 0 at the start of your method). I can see you are incrementing i at the end, but its of no use as its not in a loop.
I can think of a solution as to find the index of a in string, then take substring from that index + 3 and look for z. Something like:
s = "wwwaeezdddddd"
s[s.index("a")..s.index("a")+3]
#=> "aeez"
s[s.index("a")..s.index("a")+3] =~ /z/ # checking if z is present
#=> 3
If a can occur more than once in input String, you need to find all indices of a and run the above logic in a loop. Something like:
s = "wwwaesezddddddaz"
indexes = (0 ... s.length).find_all { |i| s[i,1] == 'a' }
#=> [3, 14]
indexes.each { |i| break if #is_present = s[i..i+3] =~ /z/ }
#is_present
#=> 1
Let’s implement the FSM ourselves :)
input = "wwwaeezdddddd"
!(0...input.length).each do |idx|
next unless input[idx] == 'a' # skip unrelated symbols
current = (idx..[idx + 3, input.length - 1].min).any? do |i|
input[i] == 'z' # return true if there is 'z'
end
# since `each` returns truthy (range itself),
# in case of success we return falsey and negate
break false if current
end
#⇒ true
Please note, that the above implementation is O(length(input)) and does not use any built-in ruby helpers, it is just iterating a string char by char.
While the regexp solution is the most elegant, here is one for completion, which is more in spirit to your original attempt:
def nearby_az(string)
!!(apos = string.index('a') and string[apos,3].index('z'))
end

How to count the number of characters between two characters in a string

This is the question's prompt:
Write a method that takes a string and returns true if the letter
"z" appears within three letters after an "a". You may assume
that the string contains only lowercase letters.
I'm trying to use the ternary operator, and want to include the match or count methods. Any idea on how I can find the number of characters between "a" and "z" or the simplest way to solve this?
def nearby_az(string)
string.count <= 3 ? true : false
end
Regex would be a good way to solve this problem.
You can use online regex testers to experiment with different regexes, inputs and outputs.
The first solution that comes to my mind is to come up with a pattern for each possible correct input:
az
a[a-z]z
a[a-z][a-z]z
Which means:
Match the string "az"
Match a string with "a" and then a character from "a" to "z" and then a "z" character
Match a string with an "a" and then 2 characters from "a" to "z" and then a "z"
and then combine them with the 'or' operator (|)
az|a[a-z]z|a[a-z][a-z]z
Which means match on all three of those conditions.
A link to this example is here.
Doing it this way is a bit verbose so it can be improved by expressing this in a more compact way:
a[a-z]{0,2}z
This means:
Match an "a" then match a character from "a" to "z" 0, 1 or 2 times and then match a "z"
A link to this example is here
You use the method on ruby strings called match which takes in a regex object and then check the boolean return value.
Edit:
The ruby code would look something like this:
def nearby_az(string)
return string.match(/a[a-z]{0,2}z/) != nil
end
string.match() returns an object that you can query to get information about the match. If there is no match, string.match() will return nil.
!!("fjeioaeiz" =~ /a.{,2}z/) #=> true
!!("fjeioaz" =~ /a.{,2}z/) #=> true
!!("fjeioasbdz" =~ /a.{,2}z/) #=> false
Look, Ma! No regex!
def a_upto_4_z(str)
str.each_char.with_index.any? { |c,i| c == ?a && str[i+1,3].include?(?z) }
end
a_upto_4_z "rvaxxzo" #=> true
a_upto_4_z "rvaxxxzo" #=> false
a_upto_4_z "rvaxzo" #=> true
a_upto_4_z "rvazo" #=> true
a_upto_4_z "rvzao" #=> false
Edit: #Stefan makes a good point. Let's do it this way:
def mind_the_gap(str, max_gap=2)
gap = max_gap + 1 # or larger
str.each_char do |c|
case c
when ?z
return true if gap <= max_gap
when ?a
gap = 0
else
gap += 1
end
end
false
end
mind_the_gap "rvaxxzo" #=> true
mind_the_gap "rvaxxxzo" #=> false
mind_the_gap "rvaxzo" #=> true
mind_the_gap "rvazo" #=> true
mind_the_gap "rvzao" #=> false
Note it is not necessary to increment gap when c == ?z and gap > max_gap.

How can I check to see if a pair of words are only 1 letter different?

In writing a method to compare 2 words, how can I check to see if the words are only 1 letter different? I'm assuming words are same length and order of letters doesnt matter (see "cobra","bravo").
def one_letter_apart?(word1, word2)
I expect the results below:
one_letter_apart?("abra","abro") == true
one_letter_apart?("cobra","bravo") == true
one_letter_apart?("bravo","tabby") == false
one_letter_apart?("abc","cab") == false
I have tried a few ways of manipulating them (splitting,sorting,then setting equal and adding to new array, then counting), but none so far have worked. Any ideas are greatly appreciated.
This one makes use of the fact that String#sub substitutes only the first thing it finds.
def one_different_char?(str, other)
other_str = other.dup
str.chars{|char| other_str.sub!(char, '')} #sub! just replaces just one occurence of char
other_str.size == 1
end
test_set = [["abra","abro"],["cobra","bravo"],["bravo","tabby"],["abc","cab"]]
test_set.each{|first, second| puts one_different_char?(first, second) }
#true
#true
#false
#false
Check Levenshtein Distance
You want the Levenstein distance. For example, using the text gem:
require 'text'
def one_letter_apart? string1, string2
Text::Levenshtein.distance(string1, string2).eql? 1
end
one_letter_apart? "abra", "abro"
# => true
one_letter_apart? "cobra", "bravo"
# => false
def one_letter_apart?(s1, s2)
return false if s1.length != s2.length
a2 = s2.chars.to_a
s1.chars.each do |c|
if i = a2.index(c)
a2.delete_at(i)
end
end
a2.length == 1
end
one_letter_apart?("abra","abro") == true
# => true
one_letter_apart?("cobra","bravo") == true
# => true
one_letter_apart?("bravo","tabby") == false
# => true
one_letter_apart?("abc","cab") == false
# => true
Update: To answer your question of how it works: This is the exact same general algorithm as steenslag's, but I didn't think of using String#sub! to do the removal, so I converted to arrays and used a combination of index and delete_at to remove the first occurrence of the given character. The naïve approach is a2.delete_at(a2.index(c)), but if the character c doesn't exist in a2, then index returns nil, which is an invalid input for delete_at. The workaround is to only call delete_at if index returns something non-nil, which is what I've done. i is declared and set to a2.index(c), and the value of that assignment is evaluated by if. It's the same as:
i = a2.index(c)
if i
# ...
I much prefer steenslag's approach and would have done the exact same thing if I'd thought of String#sub!.
This function returns true if two strings have equal lengths and only one different letter while all the other letters are in the same positions:
def one_letter_apart? string1, string2
return false if string1.size != string2.size
found = false
(0...string1.size).each do |i|
next if string1[i] == string1[i]
return false if found # if found is already true, and we found another difference, then result is false.
found = true # We found the first difference.
end
found # True if only one difference was found.
end
This function handles letters in wrong positions (like "cobra" and "bravo") as well:
def one_letter_apart? string1, string2
letters1 = string1.chars.each_with_object(Hash.new(0)) { |c, h| h[c] += 1 }
letters2 = string2.chars.each_with_object(Hash.new(0)) { |c, h| h[c] -= 1 }
diff = letters1.merge(letters2) { |key, count1, count2| count1 + count2 }
return diff.values.select { |v| v != 0 } .sort == [-1, 1]
end

Separating a code chunk into the main parts and the expected return parts

What is the best way to separate a code chunk (string) into its "main parts" and its "expected return parts"? Here are my definitions:
An expected return part is a line that matches /^[ \t]*#[ \t]*=>/ followed by zero or more consecutive lines that do not match /^[ \t]*#[ \t]*=>/ but match /[ \t]*#(?!\{)/.
A main part is any consecutive lines that is not an expected return part.
Main parts and expected return parts may appear multiple times in a code chunk.
Given a string of code chunk, I want to get an array of arrays, each of which includes a flag of whether it is an expected return part, and the string. What is the best way to do this? For example, given a string code whose content is:
def foo bar
"hello" if bar
end
#=> foo(true) == "hello"
#=> foo(false) == nil
a = (0..3).to_a
#=> a == [
# 0,
# 1,
# 2,
# 3
# ]
I would like a return that would be equivalent to this:
[[false, <<CHUNK1], [true <<CHUNK2], [true, <<CHUNK3], [false, <<CHUNK4], [true, <<CHUNK5]]
def foo bar
"hello" if bar
end
CHUNK1
#=> foo(true) == "hello"
CHUNK2
#=> foo(false) == nil
CHUNK3
a = (0..3).to_a
CHUNK4
#=> a == [
# 0,
# 1,
# 2,
# 3
# ]
CHUNK5
This regex should match all expected returns:
^([ \t]*#[ \t]*=>.+(?:\n[ \t]*#(?![ \t]*=>).+)*)
Extract and then replace all expected returns from your string with a separator. Then split your string by the separator and you will have all main parts.
Test it here: http://rubular.com/r/ZYjqPQND28
There is a slight problem with your definition pertaining to the regex /[ \t]*#(?!>\{)/, by which I am assuming you meant /[ \t]*#(?!=>)/, because otherwise
#=> foo(true) == "hello"
#=> foo(false) == nil
would count as one chunk
Another approach would be to use this regex (completely unoptimised):
^([ \t]*#[ \t]*=>.+(?:\n[ \t]*#(?![ \t]*=>).+)*|(?:[ \t]*(?!#[ \t]*=>).+\n)*)
to simply split it into chunks correctly, then do a relatively simple regex test on each chunk to see if it is an expected return or main part.

How do I find the index of a character in a string in Ruby?

If I have str = 'abcdefg', how do I find the index of c in this string using Ruby?
index(substring [, offset]) → fixnum or nil
index(regexp [, offset]) → fixnum or nil
Returns the index of the first occurrence of the given substring or pattern (regexp) in str. Returns nil if not found. If the second parameter is present, it specifies the position in the string to begin the search.
"hello".index('e') #=> 1
"hello".index('lo') #=> 3
"hello".index('a') #=> nil
"hello".index(?e) #=> 1
"hello".index(/[aeiou]/, -3) #=> 4
Check out ruby documents for more information.
You can use this
"abcdefg".index('c') #=> 2
str="abcdef"
str.index('c') #=> 2 #String matching approach
str=~/c/ #=> 2 #Regexp approach
$~ #=> #<MatchData "c">
Hope it helps. :)

Resources