how to check if two string contains the same character in ruby - ruby

I want to make a word unscarambler in ruby. say if I have a words in array
words = ["foo","ofo"]
how can I compare this to another string like "oof" and returning true value

This can be done as follows.
words = ["foo", "ofo", "goo"]
target = "foo"
target_size = target.size
#=> 3
target_sorted = target.each_char.sort
#=> ["f", "o", "o"]
words.select { |w| anagram?(target_size, target_sorted, w) }
#=> ["foo", "ofo"]
The typical way anagram? is written is:
def anagram?(target_size, target_sorted, w)
return false unless w.size == target_size
w.each_char.sort == target_sorted
end
However, I've wondered it might be faster to:
Step through the characters of target
Search for the index i of a matching character in w
If a match is found, delete w[i]
if no match is found (i #=> nil), return false
return true if false is not returned earlier
This can be implemented thus:
def anagram?(target_size, target, w)
return false unless target.size == w.size
wcpy = w.dup
target.each_char do |c|
i = wcpy.index(c)
return false unless i
wcpy[i] = ''
end
true
end
words.select { |w| anagram?(target_size, target, w) }
#=> ["foo", "ofo"]
I'll have to benchmark the two one day.
We could also write:
def anagram?(w1, w2)
return false unless w1.size == w2.size
w1.chars.difference(w2.chars).empty?
end
The helper Array#difference is defined here.

If all the strings in the array are permutations of each other then:
words = ["foo", "ofo"]
str = "foo"
words[0].split("").sort == str.split("").sort

Related

How to get the returned value from a method from within the method?

I've been practicing some algorithms with ruby for a while, and I'm wondering if it is possible to catch the returned value from within the method.
the code below is to reverse a string without any kind of reverse method and with few local variables...
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
end
Note that the result of the 'each' method is not being assigned to any variable. So, 'each' evaluates to an array with a reversed sequence of characters. At the 'end' (literally) I've just 'called' the method 'join' to glue everything together. The idea is to 'catch' the returned value from all this process and check if is true or false that the reversed string is a palindrome.
If the reversed string is equal to the original one then the word is a palindrome. Ex. "abba", "sexes", "radar"...
for example:
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
# catch here the returned value from the code above
# and check if its a palindrome or not. (true or false)
end
Thank you guys! I will be very grateful if anyone could help me figure out this!
Just add == a to see if your reversal matches the original string:
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join == a
end
puts rev("racecar") # => true
puts rev("racecars") # => false
An easier way to check palindromes (rev could be better named palindrome?) is a == a.reverse since .reverse is essentially what your split/each/join does.
If you want back all the information, you can return an array with both the values:
def rev(a)
i = -1
rev = a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
[rev, rev == a] # or
# return rev, rev == a
end
p rev("abra") #=> ["arba", false]
p rev("abba") #=> ["abba", true]
You can also return a hash:
{ reverse: rev, palindrome: rev == a}
to get
#=> {:reverse=>"arba", :palindrome=>false}
#=> {:reverse=>"abba", :palindrome=>true}
Here are a couple of other ways you could reverse a string.
#1
def esrever(str)
s = str.dup
(str.size/2).times { |i| s[i], s[-1-i] = s[-1-i], s[i] }
s
end
esrever("abcdefg")
#=> "gfedcba"
esrever("racecar")
#=> "racecar"
This uses parallel assignment (sometimes called multiple assignment).
#2
def esrever(str)
a = str.chars
''.tap { |s| str.size.times { s << a.pop } }
end
esrever("abcdefg")
#=> "gfedcba"
esrever("racecar")
#=> "racecar"
I've used Object#tap merely to avoid creating a local variable initialized to an empty string and then having to make that variable the last line of the method.
With both methods a string str is a palindrome if and only if str == esrever(str).

Substring in string

This function should take in two strings "daBcD" and "ABC". It is trying to create the string "b" from the letters in "a". You can only delete or capitalize letters, you cant change them. b will always contain all uppercase letters.
def abbreviation(a, b)
aArray = a.split('')
idx = 0
aArray.each do |char|
#print "char: #{char}\n"
#print "Before loops: #{aArray}\n"
if char.casecmp(b[idx]) == 0
char.upcase!
idx += 1
#print "char: #{char}\nArry: #{aArray}\n"
#print "idx: #{idx}\n siz: #{b.size}\n"
if idx == b.size
aArray.reject! {|i| i == 'delete'}
aArray.slice!(b.size)
break
end
else
aArray[aArray.index(char)] = 'delete'
#print "deleted, now is: #{aArray}\n"
end
end
res = aArray.join('')
if res == b
return 'YES'
else
return 'NO'
end
end
This works for a couple test cases, but fails most of them. Can someone describe a better approach?
I have assumed the problem is to determine whether the characters in b appear in a (case indifferent), in the same order as in b, but not necessarily contiguous in a (see the second example below). If they do I return an array of the indices at which they appear in a. If there is no match, nil is returned.
def doit(a, b)
m = a.match(Regexp.new(b.each_char.map { |c| "(#{c})" }.join('.*'),
Regexp::IGNORECASE))
return nil if m.nil?
(1..b.size).map { |i| m.begin(i) }
end
doit "daBcD", "ABC"
#=> [1, 2, 3]
doit "daXBDecf", "ABC"
#=> [1, 3, 6]
doit "dacBD", "ABC"
#=> nil
For the first example the regular expression is as follows.
Regexp.new("ABC".each_char.map { |c| "(#{c})" }.join('.*'), Regexp::IGNORECASE)
#=> /(A).*(B).*(C)/i
The absolutely easiest way is via regular expression:
def abbreviation(a, b)
re = Regexp.new(b.each_char.map(&Regexp.method(:quote)).join('.*'), Regexp::IGNORECASE)
!!re.match(a)
end
abbreviation("daBcD", "ABC")
# => true
abbreviation("daCbD", "ABC")
# => false
For the input ABC, we'll construct a regular expression /A.*B.*C/i, then test the other string against it. The .* construct will account for "deletion"; the IGNORECASE option for "capitalisation".
EDIT: If the problem is further constrained that only lowercase letters can be deleted, as suggested by the comments,
def abbreviation(a, b)
# (b is uppercase only)
re_pat = b.each_char.map { |c| "[#{c}#{c.downcase}]"}.join('[[:lower:]]*')
re = Regexp.new(re_pat)
!!re.match(a)
end
p abbreviation("daBcD", "ABC") # => true
p abbreviation("daBdcD", "ABC") # => true
p abbreviation("daBDcD", "ABC") # => false

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.

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

Can't get Ruby method to pass spec for Anagram method

I'm trying to get this to pass spec to verify if an argument is an anagram of another word, but it's just not happening.
I can get the string (starting with just one sting word) into an array, and whether it's one or multiple words,
It then iterates through the array over each word.
Using the If statement to compare if the sorted object is equal to the sorted argument.
Applied .join, since it came out one letter at a time in irb, but it's still not happening, with or without .join.
class String
define_method(:anagrams) do |check_word|
words = self.downcase
check_word = check_word.downcase
words_array = words.split(" ")
words_array.each do |word|
if (word.chars.sort) == (check_word.chars.sort)
true
else
false
end
end
end
end
Any ideas why it's broken?
words_array.each do |word|
if (word.chars.sort) == (check_word.chars.sort)
true
else
false
end
end
I'm assuming you want to return true if any words are anagrams. You're currently not explicitly returning.
Better Ruby syntax would be words_array.any? { |word| word.chars.sort == check_word.chars.sort) }
OR
words_array.each do |word|
return true if (word.chars.sort) == (check_word.chars.sort)
end
Here's another way to see if two words w1 and w2 are anagrams of each other:
def anagrams?(w1, w2)
w1.size == w2.size && w1.chars.difference(w2.chars).empty?
end
where Array#difference is how I defined it in my answer here.
Array#difference is similar to Array#-. The difference is illustrated in the following example:
a = [1,2,3,4,3,2,2,4]
b = [2,3,4,4,4]
a - b #=> [1]
a.difference b #=> [1, 3, 2, 2]
Let's try it:
anagrams?("tops", "stop") #=> true
anagrams?("tops", "stopz") #=> false
anagrams?("tops", "stopz") #=> false
anagrams?("tops", "sto") #=> false

Resources