Match a pattern in an array - ruby

There is an array with 2 elements
test = ["i am a boy", "i am a girl"]
I want to test if a string is found inside the array elements, say:
test.include("boy") ==> true
test.include("frog") ==> false
Can i do it like that?

Using Regex.
test = ["i am a boy" , "i am a girl"]
test.find { |e| /boy/ =~ e } #=> "i am a boy"
test.find { |e| /frog/ =~ e } #=> nil

Well you can grep (regex) like this:
test.grep /boy/
or even better
test.grep(/boy/).any?

Also you can do
test = ["i am a boy" , "i am a girl"]
msg = 'boy'
test.select{|x| x.match(msg) }.length > 0
=> true
msg = 'frog'
test.select{|x| x.match(msg) }.length > 0
=> false

I took Peters snippet and modified it a bit to match on the string instead of the array value
ary = ["Home:Products:Glass", "Home:Products:Crystal"]
string = "Home:Products:Glass:Glasswear:Drinking Glasses"
USE:
ary.partial_include? string
The first item in the array will return true, it does not need to match the entire string.
class Array
def partial_include? search
self.each do |e|
return true if search.include?(e.to_s)
end
return false
end
end

If you don't mind to monkeypatch the the Array class you could do it like this
test = ["i am a boy" , "i am a girl"]
class Array
def partial_include? search
self.each do |e|
return true if e[search]
end
return false
end
end
p test.include?("boy") #==>false
p test.include?("frog") #==>false
p test.partial_include?("boy") #==>true
p test.partial_include?("frog") #==>false

If you want to test if a word included into the array elements, you can use method like this:
def included? array, word
array.inject([]) { |sum, e| sum + e.split }.include? word
end

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

how to check if two string contains the same character in 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

How to replace text in a ruby string

I am trying to write a very simple method in Ruby which takes a string and an array of words and checks if the string contains any of the words and if it does it replaces them with their uppercase.
I made an attempt but its not great due to my level of Ruby skills.
def(my_words,my_sentence)
#split the sentence up into an array of words
my_sentence_words = my_sentence.split(/\W+/)
#nested loop that checks the words array for each brand
my_sentence_words.each do |i|
my_words.each do |i|
#if it finds a brand in the words and sets them to be uppercase
if my_words[i] == my_sentence_words[i]
my_sentence_words[i] == my_sentence_words[i].up.case
end
end
end
#put the words array into one string
words.each do |i|
new_sentence = ("" + my_sentence_words[i]) + " "
end
end
I am getting: can't convert string into integer error
def convert(mywords,sentence)
regex = /#{mywords.join("|")}/i
sentence.gsub(regex) { |m| m.upcase }
end
convert(%W{ john james jane }, "I like jane but prefer john")
#=> "I like JANE but prefer JOHN"
This will work better. It loops through the brands, searches for each, and replaces with the uppercase version.
brands = %w(sony toshiba)
sentence = "This is a sony. This is a toshiba."
brands.each do |brand|
sentence.gsub!(/#{brand}/i, brand.upcase)
end
Results in the string.
"This is a SONY. This is a TOSHIBA."
For those who like Ruby foo!
sentence.gsub!(/#{brands.join('|')}/i) { |b| b.upcase }
And in a function
def capitalize_brands(brands, sentence)
sentence.gsub(/#{brands.join('|')}/i) { |b| b.upcase }
end
You get this error because i doesn't start from 0 as you expected, in each method i is an element of array, and has string type, it's a first word from your sentence:
my_sentence_words = ["word"]
my_sentence_words.each do |i|
puts i.length #=> 4
puts i.type #=> String
puts i #=> word
end
So you try to call my_sentence_words[word] instead of my_sentence_words[0]. You can try method each_index that passes index of element instead of element itself`:
def check(str, *arr)
upstr = str.split(' ')
upstr.eachindex do |i| #=> i is index
arr.each_index do |j|
upstr[i].upcase! if upstr[i] == arr[j]
end
end
upstr
end
check("This is my sentence", "day", "is", "goal", "may", "my")
#=>["This", "IS", "MY", "sentence"]

ruby string to hash conversion

I have a string like this,
str = "uu#p, xx#m, yy#n, zz#m"
I want to know how to convert the given string into a hash. (i.e my actual requirement is, how many values (before the # symbol) have the m, n and p. I don't want the counting, I need an exact value). The output would be better like this,
{"m" => ["xx", "zz"], "n" => ["yy"], "p" => ["uu"]}
Can help me anyone, please?
Direct copy/past of an IRB session:
>> str.split(/, /).inject(Hash.new{|h,k|h[k]=[]}) do |h, s|
.. v,k = s.split(/#/)
.. h[k] << v
.. h
.. end
=> {"p"=>["uu"], "m"=>["xx", "zz"], "n"=>["yy"]}
Simpler code for a newbie :)
str = "uu#p, xx#m, yy#n, zz#m"
h = {}
str.split(",").each do |x|
v,k = x.split('#')
h[k] ||= []
h[k].push(v)
end
p h
FP style:
grouped = str
.split(", ")
.group_by { |s| s.split("#")[1] }
.transform_values { |ss| ss.map { |x| s.split("#")[0] } }
#=> {"m"=>["xx", "zz"], "n"=>["yy"], "p"=>["uu"]}
This is a pretty common pattern. Using Facets.map_by:
require 'facets'
str.split(", ").map_by { |s| s.split("#", 2).reverse }
#=> {"m"=>["xx", "zz"], "n"=>["yy"], "p"=>["uu"]}

Resources