Getting a value from a string - ruby

I am trying to match one or more keywords within a string (str), but I have no luck. My method below is trying to match all keys when I only needed to match any (one or more).
#str = "g stands for girl and b is for boy"
def key1
%w[a, b, c, d, f, g]
end
def result
if #str.include?( key1 )
puts "happy days"
else
puts "bad days"
end
end
puts result # => bad days
How to make it show "happy days"?
PS: I have no idea what to name this title to. Maybe a mod could rename it?

You're asking if your string includes the Array: ["a", "b", "c", "d", "f", "g"]
What I think you're trying to ask is this: are there any elements in the array that also exist in the string? This is a good use case for Enumerable#any like this:
[2] pry(main)> #str = "g stands for girl and b is for boy"
=> "g stands for girl and b is for boy"
[3] key1 = %w[a b c d f g]
=> ["a", "b", "c", "d", "f", "g"]
[4] pry(main)> key1.any? { |letter| #str.include?(letter) }
=> true
So to refactor your code, it might look like this:
#str = "g stands for girl and b is for boy"
def key1
%w[a b c d f g]
end
def result
if key1.any? { |letter| #str.include?(letter) }
puts "happy days"
else
puts "bad days"
end
end
Something to note, with %w you don't need to use commas, you can simply separate the letters by a space (as outlined above).

I can't clearly understand your question, but I suspect you are looking for following:
key1.find { |k| #str.include? k }

I would use a regular expression:
MAGIC_CHARS = %w[a b c d f g]
#=> ["a", "b", "c", "d", "f", "g"]
def result(str)
(str =~ /#{MAGIC_CHARS.join('|')}/) ? "happy days" : "bad days"
end
result("g stands for girl and b is for boy")
#=> "happy days"
result("nothin' here")
#=> "bad days"
Note:
/#{MAGIC_CHARS.join('|')}/
#=> /a|b|c|d|f|g/
You could instead write:
Regexp.union(MAGIC_CHARS.map { |c| /c/ })
#=> /(?-mix:c)|(?-mix:c)|(?-mix:c)|(?-mix:c)|(?-mix:c)|(?-mix:c)/
or
/[#{MAGIC_CHARS.join('')}]/
#=> /[abcdfg]/
Another way:
def result(str)
(str.chars & MAGIC_CHARS).any? ? "happy days" : "bad days"
end
result("g stands for girl and b is for boy")
#=> "happy days"
result("nothin' here")
#=> "bad days"

Related

Verifying if a string (several words) in an array of strings matches with another string in ruby

I am a beginner in ruby. I have an array of strings like this ["a b c", "d e f"] and I have a string like this "xapqbrc". I want to verify if "xapqbrc" contains all the words in each string but not necessarily one next to other. How can I do that in ruby?
["a b c", "d e f"].include? "xapqbrc"
is not working as expected
include? just checks if any object equals any object in the Array. ["a b c", "d e f"].include? "xapqbrc" would only be true if the whole string "xapqbrc" was in the Array.
Splitting this into two parts, first checking if one string contains all the words in another. First, split the string of words up into an Array.
words = "a b c".split(/ /) # ["a", "b", "c"]
Now we can use include? but on a String to check if the String contains another string. "food".include?("foo") is true. But that's only for one word, we need to do this for all words. Use all? to check if a thing is true for all items in an Array.
words.all? { |word| "xapqbrc".include?(word) }
Finally, we need to do this for an Array of those words. We can use select to get only the items in the Array for which the block is true.
# ["a b c"]
matches = ["a b c", "d e f"].select { |string|
words = string.split(/ /)
# The last statement in the block determines the truthiness of the block.
words.all? { |word| "xapqbrc".include?(word) }
}
To check if all characters in a string occur in the arrays of strings:
('xapqbrc'.chars - ['a b c', 'd e f'].map(&:chars).flatten).empty?
# => false
Here is a breakdown of the above statement.
The method chars returns the array of all characters in a string. Note that for a string with blanks, this array will include blanks. If you want to extract the characters separated by blanks, or, more generally, whitespace, use split like so (as in the answer by Schwern):
['a b c', 'd e f'].map{ |s| s.split(/ /) }.flatten
The method flatten takes the array of arrays and flattens it into a single array.
puts 'xapqbrc'.chars.inspect
# => ["x", "a", "p", "q", "b", "r", "c"]
puts ['a b c', 'd e f'].map(&:chars).flatten.inspect
# => ["a", " ", "b", " ", "c", "d", " ", "e", " ", "f"]
puts ('xapqbrc'.chars - ['a b c', 'd e f'].map(&:chars).flatten).inspect
# => ["x", "p", "q", "r"]
puts ('xapqbrc'.chars - ['a b c', 'd e f'].map(&:chars).flatten).empty?
# => false
A few more examples:
strings1 = ['a b c', 'd e f']
['xapqbrc', 'accbbb', 'xpq', ' ', ''].each do |string2|
puts "string2=#{string2};"
puts (string2.chars - strings1.map(&:chars).flatten).empty?
end
# => string2=xapqbrc;
# => false
# => string2=accbbb;
# => true
# => string2=xpq;
# => false
# => string2= ;
# => true
# => string2=;
# => true

Why the Ruby each iterator goes first in the execution?

I've came across a weird thing doing simple tasks in Ruby. I just want to iterate the alphabet with the each method but the iteration goes first in the execution:
alfawit = ("a".."z")
puts "That's an alphabet: \n\n #{ alfawit.each { |litera| puts litera } } "
and this code results in this: (abbreviated)
a
b
c
⋮
x
y
z
That's an alphabet:
a..z
Any ideas why it works like this or what supposedly I did wrong?
Thanks in advance.
Because your each call is interpolated in your string literal that's executed before the fixed string. Also, each returns an Enumerable, in fact you print even that. Try this one
alfawit = ("a".."z")
puts "That's an alphabet: \n\n"
alfawit.each { |litera| puts litera }
or
puts "That's an alphabet: \n\n"
("a".."z").each { |litera| puts litera }
you can use interpolation if you want but in this way
alfawit = ("a".."z")
puts "That's an alphabet: \n\n#{alfawit.to_a.join("\n")}"
You can easily see what's going on if you extract the interpolation part into a variable:
alfawit = ("a".."z")
foo = alfawit.each { |litera| puts litera }
puts "That's an alphabet: \n\n #{ foo } "
The second line is causing the trouble: each invokes the block for each element of the range and then returns the receiver, so that foo becomes alfawit.
Here's another way to get the desired result:
alfawit = "a".."z"
puts "That's an alphabet:", alfawit.to_a
puts outputs each argument on a new line, but for array arguments, it outputs each element on a new line. Result:
That's an alphabet:
a
b
c
⋮
x
y
z
Likewise, you can turn the range into an argument list via *:
alfawit = "a".."z"
puts "That's an alphabet:", *alfawit
That's equivalent to:
puts "That's an alphabet:", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"

Ruby string char chunking

I have a string "wwwggfffw" and want to break it up into an array as follows:
["www", "gg", "fff", "w"]
Is there a way to do this with regex?
"wwwggfffw".scan(/((.)\2*)/).map(&:first)
scan is a little funny, as it will return either the match or the subgroups depending on whether there are subgroups; we need to use subgroups to ensure repetition of the same character ((.)\1), but we'd prefer it if it returned the whole match and not just the repeated letter. So we need to make the whole match into a subgroup so it will be captured, and in the end we need to extract just the match (without the other subgroup), which we do with .map(&:first).
EDIT to explain the regexp ((.)\2*) itself:
( start group #1, consisting of
( start group #2, consisting of
. any one character
) and nothing else
\2 followed by the content of the group #2
* repeated any number of times (including zero)
) and nothing else.
So in wwwggfffw, (.) captures w into group #2; then \2* captures any additional number of w. This makes group #1 capture www.
You can use back references, something like
'wwwggfffw'.scan(/((.)\2*)/).map{ |s| s[0] }
will work
Here's one that's not using regex but works well:
def chunk(str)
chars = str.chars
chars.inject([chars.shift]) do |arr, char|
if arr[-1].include?(char)
arr[-1] << char
else
arr << char
end
arr
end
end
In my benchmarks it's faster than the regex answers here (with the example string you gave, at least).
Another non-regex solution, this one using Enumerable#slice_when, which made its debut in Ruby v.2.2:
str.each_char.slice_when { |a,b| a!=b }.map(&:join)
#=> ["www", "gg", "fff", "w"]
Another option is:
str.scan(Regexp.new(str.squeeze.each_char.map { |c| "(#{c}+)" }.join)).first
#=> ["www", "gg", "fff", "w"]
Here the steps are as follows
s = str.squeeze
#=> "wgfw"
a = s.each_char
#=> #<Enumerator: "wgfw":each_char>
This enumerator generates the following elements:
a.to_a
#=> ["w", "g", "f", "w"]
Continuing
b = a.map { |c| "(#{c}+)" }
#=> ["(w+)", "(g+)", "(f+)", "(w+)"]
c = b.join
#=> "(w+)(g+)(f+)(w+)"
r = Regexp.new(c)
#=> /(w+)(g+)(f+)(w+)/
d = str.scan(r)
#=> [["www", "gg", "fff", "w"]]
d.first
#=> ["www", "gg", "fff", "w"]
Here's one more way of doing it without a regex:
'wwwggfffw'.chars.chunk(&:itself).map{ |s| s[1].join }
# => ["www", "gg", "fff", "w"]

How do I create a histogram by iterating over an array in Ruby

So I was told to rewrite this question and outline my goal. They asked me to iterate over the array and "Use .each to iterate over frequencies and print each word and its frequency to the console... put a single space between the word and its frequency for readability."
puts "Type something profound please"
text = gets.chomp
words = text.split
frequencies = Hash.new 0
frequencies = frequencies.sort_by {|x,y| y}
words.each {|word| frequencies[word] += 1}
frequencies = frequencies.sort_by{|x,y| y}.reverse
puts word +" " + frequencies.to_s
frequencies.each do |word, frequencies|
end
Why can't it convert the string into an integer? What am I doing incorrectly?
Try this code:
puts "Type something profound please"
words = gets.chomp.split #No need for the test variable
frequencies = Hash.new 0
words.each {|word| frequencies[word] += 1}
words.uniq.each {|word| puts "#{word} #{frequencies[word]}"}
#Iterate over the words, and print each one with it's frequency.
I'd do as below :
puts "Type something profound please"
text = gets.chomp.split
I called here Enumerable#each_with_object method.
hash = text.each_with_object(Hash.new(0)) do |word,freq_hsh|
freq_hsh[word] += 1
end
I called below Hash#each method.
hash.each do |word,freq|
puts "#{word} has a freuency count #{freq}"
end
Now run the code :
(arup~>Ruby)$ ruby so.rb
Type something profound please
foo bar foo biz bar baz
foo has a freuency count 2
bar has a freuency count 2
biz has a freuency count 1
baz has a freuency count 1
(arup~>Ruby)$
chunk is a good method for this. It returns an array of 2-element arrays. The first of each is the return value of the block, the second is the array of original elements for which the block returned that value:
words = File.open("/usr/share/dict/words", "r:iso-8859-1").readlines
p words.chunk{|w| w[0].downcase}.map{|c, words| [c, words.size]}
=> [["a", 17096], ["b", 11070], ["c", 19901], ["d", 10896], ["e", 8736], ["f", 6860], ["g", 6861], ["h", 9027], ["i", 8799], ["j", 1642], ["k", 2281], ["l", 6284], ["m", 12616], ["n", 6780], ["o", 7849], ["p", 24461], ["q", 1152], ["r", 9671], ["s", 25162], ["t", 12966], ["u", 16387], ["v", 3440], ["w", 3944], ["x", 385], ["y", 671], ["z", 949]]

Using each_with_index with map

I want to take a array and make it an order list. Currently I'm trying to do it in this way:
r = ["a", "b","c"]
r.each_with_index { |w, index| puts "#{index+1}. #{w}" }.map.to_a
# 1. a
# 2. b
# 3. c
#=> ["a", "b", "c"]
the output should be ["1. a", "2. b", "3. c"].
How do I get the proper output to be the new value for the r array?
a.to_enum.with_index(1).map { |element, index| "#{index}. #{element}" }
or
a.map.with_index(1) { |element, index| "#{index}. #{element}" }
with_index(1) makes the index of the first element 1.
In the first solution the array is converted to an enum, and in the second solution the array is directly mapped.
> => r.each_with_index.map { |w, index| "#{index+1}. #{w}" }
> => ["1. a", "2. b", "3. c"]
You need to map first, then puts:
r = %w[a b c]
r.map.with_index do |w, index|
"#{index + 1}. #{w}"
end.each do |str|
puts str
end
#=> ["1. a", "2. b", "3. c"]
# prints:
# 1. a
# 2. b
# 3. c
This is because each (and each_with_index) simply returns the original array.

Resources