Search for a specific item in a Array - ruby

I want to search through the array:
letters = ["a", "b", "c", "d", "e"]
to see if "b" is in it; if it is, then it should say yes. I understand:
letters[0..0] == ["a"]
I tried this:
if letters[0..5] == ["b"]
puts "Yes, the letter 'b' in there."
else
puts "No 'b' in the array."
end

There's an in-build method to do that:
letters.include? "b"

Try: http://www.ruby-doc.org/core-2.1.3/Array.html#method-i-include-3F
if letters.include?("b")
puts "Yes, the letter 'b' in there."
else
puts "No 'b' in the array."
end

if letters.index('b')
puts "yes"
else
puts "no"
end

Related

.any? inside a case block not evaluating as expected

I have the following situation:
type = "stringX"
someArray = ["stringX", "string1", "string2"]
case type
when "stringA"
puts "a"
when "stringB"
puts "b"
when someArray.any? { |x| x.include?(type) }
puts "x"
when "stringC"
puts "c"
end
What I was expecting to happen was that it would go through the case and once it evaluates the .any? method as true (because by itself it does evaluate to true), it would puts "x". However, that's not what's happening here, it just goes through the rest of the case and reaches a raise somewhere below that.
I'm wondering what's going on here?
Use * operator
value = "stringX"
some_array = ["stringX", "string1", "string2"]
case type
when "stringA"
puts "a"
when "stringB"
puts "b"
when *some_array # notice the * before the variable name!
puts "x"
when "stringC"
puts "c"
end
How does this work?
when *some_array checks whether value is an element in some_array
For this particular case one should use the brilliant answer by #akuhn
Whether you need to put any random condition inside the case, you can do it using Proc#===:
type = "stringX"
someArray = ["stringX", "string1", "string2"]
case type
when "stringA" then puts "a"
when "stringB" then puts "b"
# ⇓⇓⇓⇓⇓⇓⇓⇓ HERE
when ->(type) { someArray.any? { |x| x.include?(type) } }
puts "x"
when "stringC" then puts "c"
end
EDIT: I will not delete the answer, because I think there might be something in it you didn't know before, but it does not work for your usecase. For that you should look at mudasobwas answer
It does not quite work this way, because basically the case statement will compare the given object with the object(s) passed to when, about similar to this:
if type == "stringA"
# ...
elsif type == "stringB"
# ...
and so on, unless you use an empty case statement.
case
when type == "stringA"
# ...
This is similar to an if elsif statement though, so you don't really see that very often.
In your case however, we can make use of Ruby's splat operator
case type
when "stringA"
puts "a"
when "stringB"
puts "b"
when *someArray
puts "x"
when "stringC"
puts "c"
Ruby's case statement can take multiple arguments with when which kind of works like an "or"
case "A"
when "B"
puts "B"
when "C", "A"
puts "C or A"
end
# => C or A
and the splat operator will fan out your array:
p ["a", "b"]
# => ["a", "b"]
p *["a", "b"]
# => "a"
# => "b"
p "a", "b"
# => "a"
# => "b"

How to count a string elements' occurrence in another string in ruby?

How can I check how many times a phrase occurs in a string?
For example, let's say the phrase is donut
str1 = "I love donuts!"
#=> returns 1 because "donuts" is found once.
str2 = "Squirrels do love nuts"
#=> also returns 1 because of 'do' and 'nuts' make up donut
str3 = "donuts do stun me"
#=> returns 2 because 'donuts' and 'do stun' has all elements to make 'donuts'
I checked this SO that suggests using include, but it only works if donuts is spelled in order.
I came up with this, but it doesn't stop spelling after all elements of "donuts"is spelled. i.e. "I love donuts" #=> ["o", "d", "o", "n", "u", "t", "s"]
def word(arr)
acceptable_word = "donuts".chars
arr.chars.select { |name| acceptable_word.include? name.downcase }
end
How can I check how many occurrences of donuts are there in a given string? No edge cases. Input will always be String, no nil. If it contains elements of donut only it should not count as 1 occurrence; it needs to contain donuts, doesn't have to be in order.
Code
def count_em(str, target)
target.chars.uniq.map { |c| str.count(c)/target.count(c) }.min
end
Examples
count_em "I love donuts!", "donuts" #=> 1
count_em "Squirrels do love nuts", "donuts" #=> 1
count_em "donuts do stun me", "donuts" #=> 2
count_em "donuts and nuts sound too delicious", "donuts" #=> 3
count_em "cats have nine lives", "donuts" #=> 0
count_em "feeding force scout", "coffee" #=> 1
count_em "feeding or scout", "coffee" #=> 0
str = ("free mocha".chars*4).shuffle.join
# => "hhrefemcfeaheomeccrmcre eef oa ofrmoaha "
count_em str, "free mocha"
#=> 4
Explanation
For
str = "feeding force scout"
target = "coffee"
a = target.chars
#=> ["c", "o", "f", "f", "e", "e"]
b = a.uniq
#=> ["c", "o", "f", "e"]
c = b.map { |c| str.count(c)/target.count(c) }
#=> [2, 2, 1, 1]
c.min
#=> 1
In calculating c, consider the first element of b passed to the block and assigned to the block variable c.
c = "c"
Then the block calculation is
d = str.count(c)
#=> 2
e = target.count(c)
#=> 1
d/e
#=> 2
This indicates that str contains enough "c"'s to match "coffee" twice.
The remaining calculations to obtain c are similar.
Addendum
If the characters of str matching characters target must be in the same order as those of target, the following regex could be used.
target = "coffee"
r = /#{ target.chars.join(".*?") }/i
#=> /c.*?o.*?f.*?f.*?e.*?e/i
matches = "xcorr fzefe yecaof tfe erg eeffoc".scan(r)
#=> ["corr fzefe ye", "caof tfe e"]
matches.size
#=> 2
"feeding force scout".scan(r).size
#=> 0
The questions marks in the regex are needed to make the searches non-greedy.
The solution is more or less simple (map(&:dup) is used there to avoid inputs mutating):
pattern = 'donuts'
[str1, str2, str3].map(&:dup).map do |s|
loop.with_index do |_, i|
break i unless pattern.chars.all? { |c| s.sub!(c, '') }
end
end
#⇒ [1, 1, 2]
Here's an approach with two variants, one where the letters must appear in order, and one where order is irrelevant. In both cases the frequency of each letter is respected, so "coffee" must match vs. two 'f' and two 'e' letters, "free mocha" is insufficient to match, lacking a second 'f'.
def sorted_string(string)
string.split('').sort.join
end
def phrase_regexp_sequence(phrase)
Regexp.new(
phrase.downcase.split('').join('.*')
)
end
def phrase_regexp_unordered(phrase)
Regexp.new(
phrase.downcase.gsub(/\W/, '').split('').sort.chunk_while(&:==).map do |bit|
"#{bit[0]}{#{bit.length}}"
end.join('.*')
)
end
def contains_unordered(phrase, string)
!!phrase_regexp_unordered(phrase).match(sorted_string(string.downcase))
end
def contains_sequence(phrase, string)
!!phrase_regexp_sequence(phrase).match(string.downcase)
end
strings = [
"I love donuts!",
"Squirrels do love nuts",
"donuts do stun me",
"no stunned matches",
]
phrase = 'donut'
strings.each do |string|
puts '%-30s %s %s' % [
string,
contains_unordered(phrase, string),
contains_sequence(phrase, string)
]
end
# => I love donuts! true true
# => Squirrels do love nuts true true
# => donuts do stun me true true
# => no stunned matches true false
Simple solution:
criteria = "donuts"
str1 = "I love donuts!"
str2 = "Squirrels do love nuts"
str3 = "donuts do stun me"
def strings_construction(criteria, string)
unique_criteria_array = criteria.split("").uniq
my_hash = {}
# Let's count how many times each character of the string matches a character in the string
unique_criteria_array.each do |char|
my_hash[char] ? my_hash[char] = my_hash[char] + 1 : my_hash[char] = string.count(char)
end
my_hash.values.min
end
puts strings_construction(criteria, str1) #=> 1
puts strings_construction(criteria, str2) #=> 1
puts strings_construction(criteria, str3) #=> 2

How do I capitalize and sort my input in Ruby?

My code needs the 5 words that I put in to always be in alphabetical order and I am not sure how to do it.
I already have the upcase and downcase taken care of but I also need it to sort the words I put in at the same time.
Here is what I have so far:
tasks = []
5.times do
puts "Please enter a word:"
tasks << gets.chomp
end
puts "Here are your words:"
tasks.each_with_index do |team, index|
if index.even?
puts team.upcase
else
puts team.downcase
end
end
Change
tasks.each_with_index
to
tasks.sort.each_with_index
A better rewrite of the whole code is:
tasks = Array.new(5) do
puts "Please enter a word:"
gets.chomp
end
puts "Here are your words:"
tasks.sort.each_slice(2) do |even, odd|
puts even.upcase, *(odd.downcase if odd)
end
Assuming you want the sort to be case-insensitive and want to upcase/downcase after the entries are sorted, you could do this:
which = [:upcase, :downcase].cycle
5.times.map { gets.chomp }.
sort_by(&:downcase).
map { |s| s.send(which.next) }
If the entries were "The", "cat", "and", "the", "hat", this would return:
["CAT", "hat", "IN", "the", "THE"]
Just call sort on the array:
tasks.sort.each_with_index do |team, index|
# ...
I advise to read in the docs about all the other useful methods on arrays and other core classes.

One line block and if condition

I have:
foos.each do |foo|
unless foo
puts "Foo is missing"
next
end
# rest of business logic goes here
end
I would like to write the last part of it better, something like
{ puts "Foo is missing"; next } unless foo
Unfortunately, this does not work. Does anybody know a way to write two (blocks of) commands inline with if condition?
Just use parentheses:
(puts 'a'; puts 'b') if true
#=> a
#=> b
What you are looking for can be done with parentheses:
(puts "Foo is missing"; next) unless foo
But in this particular case, it is better to write:
next puts "Foo is missing" unless foo
Use begin..end block:
begin puts "Foo is missing"; next end unless foo
foos.each { |foo| foo or ( puts "Foo is missing"; next )
# the rest of the business logic goes here
}
You can use the or syntax
[1,2,3].each do |x|
puts 'two' or next if x == 2
puts x
end
#=> 1
#=> "two"
#=> 3

Code to display alternating upper and lower-case returns lower-case only?

I am trying to write a function that displays a string as alternating upper and lower case letters.
For example:
str= "My name is ballouta!"
==> My NaMe Is BaLlOuTa!
My code is:
def alt_case
flag = 0
str = ''
self.scan(/./) do |b|
if flag == 0
b.upcase ;
flag = 1
str = str + b
else
b.downcase
flag = 0
str = str + b
end #end if
end #end do
str
end #end method
This code returns the string as lower-case ONLY.
"My name is ballouta!"
.gsub(/\w/).with_index{|s, i| i.even? ? s.upcase : s.downcase}
# => "My NaMe Is BaLlOuTa!"
You're using upcase and downcase, both of which return the altered value (which you are not saving and using).
The in-place alternatives upcase! and downcase! may help you out.
Edit: I see #bjhaid suggested pretty much the same solution as mine in a comment well before I posted this. I'll leave my answer up for the explanation I've provided.
Now that your question has been answered, let me suggest a way to change your code to make it more Ruby-like:
class String
def alt_case
split.map { |w| w.chars.map.with_index{ |s,i|
i.even? ? s.upcase : s.downcase }.join }.join(' ')
end
end
"My name is ballouta!".alt_case #=> "My NaMe Is BaLlOuTa!"
Here's how this works:
self #=> "My name is ballouta!" (default receiver)
a = self.split #=> ["My", "name", "is", "ballouta!"]
b = a.map { |w| w.chars.map.with_index{ |s,i|
i.even? ? s.upcase : s.downcase }.join }
#=> ["My", "NaMe", "Is", "BaLlOuTa!"]
b.join(' ') #=> "My NaMe Is BaLlOuTa!"
When computing b, consider the case when w => "name":
c = w.chars #=> ["n", "a", "m", "e"]
d = c.map.with_index{ |s,i| i.even? ? s.upcase : s.downcase }
#=> ["N", "a", "M", "e"]
d.join #=> "NaMe"
I added this method to the String class only because that's what you've done, but in general I wouldn't recommend that; alt_case(string) would be fine.

Resources