How to identify an index with last letter y? - arrays

I need to make a method to return an array containing all strings that are at least 6 characters long and also end with y. Ending with y is stumping me. I'm not sure how to do that.
I have tried using:
if words.length >= 6 && words[-1] == "y"
but it doesn't seem to be working.
It just returns with a blank. I'm not sure what I'm doing wrong.

As suggested by Cary Swoveland, here is a solution using Regexp, including a Benchmark:
require "benchmark/ips"
RE_1 = /\A.{5,}y\z/i
RE_2 = /.{5}y\z/i
INPUT = ["abcdey", 'mckejy', 'jay', 'jjjjjjy', 'aaaaaa', 'aaaaaY', 'aaaaaaaY'].freeze
Benchmark.ips do |x|
x.compare!
x.report("select (downcase)") do
INPUT.select{ |word| word.length >= 6 && word[-1].downcase == 'y' }
end
x.report("select (or)") do
INPUT.select{ |word| word.length >= 6 && (word[-1] == 'y' || word[-1] == 'Y') }
end
x.report("re 1") do
INPUT.select { |word| RE_1.match?(word) }
end
x.report("re 2") do
INPUT.select { |word| RE_2.match?(word) }
end
end
Comparison:
re 2: 633881.7 i/s
re 1: 615253.4 i/s - same-ish: difference falls within error
select (downcase): 499513.6 i/s - 1.27x slower
select (or): 405617.5 i/s - 1.56x slower

a=["abcdey",'mckejy','jay','jjjjjjy','aaaaaa']
Code
p a.filter{|x|x.length.eql?6 and x[-1].downcase.eql?'y'}
or
p a.select{|x|x.length.eql?6 and x[-1].downcase.eql?'y'}
Output
["abcdey", "mckejy"]
If you want to create a method then
def findTheString a
a.filter {|x| x.length.eql? 6 and x[-1].downcase.eql? 'y'}
end
a=["abcdey", 'mckejy', 'jay', 'jjjjjjy', 'aaaaaa']
p findTheString a
As Cary suggested, Here is the regular expression solution
p a.filter{|x|x[/\A.{5}(y|Y)\z/]}

Related

Skipping to next vowel and consonant in a string

I completed an assignment that has the following instructions:
Pseudocode and write a method that takes a spy's real name (e.g., "Felicia Torres") and creates a fake name with it by doing the following:
Swapping the first and last name.
Changing all of the vowels (a, e, i, o, or u) to the next vowel in 'aeiou', and all of the consonants (everything else besides the vowels) to the next consonant in the alphabet.
My solution:
#vowels = %w(a e i o u)
#consonants = ("a".."z").to_a - #vowels
def next_vowel(letter)
i = 0
while i < #vowels.length
if #vowels[i] == "u"
return #vowels[0]
elsif #vowels[i] == letter
return #vowels[i+1]
end
i += 1
end
end
def next_consonant(letter)
i = 0
while i < (#consonants.length)
if #consonants[i] == "z"
return #consonants[0]
elsif #consonants[i] == letter
return #consonants[i + 1]
end
i += 1
end
end
def alias_manager(name)
name.downcase!
first_name = name.split(" ")[0]
last_name = name.split(" ")[1]
alias_first_name = last_name.chars.map do |i|
if #vowels.include?(i)
next_vowel(i)
elsif #consonants.include?(i)
next_consonant(i)
end
end
alias_last_name = first_name.chars.map do |i|
if #vowels.include?(i)
next_vowel(i)
elsif #consonants.include?(i)
next_consonant(i)
end
end
alias_first_name.join.capitalize! + " " + alias_last_name.join.capitalize!
end
I'm trying to think of a much more succinct way of writing this. The 'while' loops don't seem like the most efficient method. I was thinking of using 'rotate' but not sure how I could replace the letter in the string. Also, is there a way to refactor the last to iterations for first_name and last_name? I'm basically writing the same thing twice for different variables.
A better way to define next_vowel and next_consonant
#vowels = %w(a e i o u)
#consonants = ("a".."z").to_a - #vowels
def next_vowel(letter)
i = #vowels.index(letter)
# Return the next vowel, using modulo for the last case (next of `u` is `a`)
#vowels[(i + 1) % #vowels.length]
end
def next_consonant(letter)
i = #consonants.index(letter)
# Return the next vowel, using modulo for the last case (next of `z` is `b`)
#consonants[(i + 1) % #consonants.length]
end
Some test case:
2.3.3 :019 > next_vowel("a")
=> "e"
2.3.3 :020 > next_vowel("e")
=> "i"
2.3.3 :021 > next_vowel("u")
=> "a"
2.3.3 :022 > next_consonant("t")
=> "v"
2.3.3 :023 > next_consonant("z")
=> "b"
2.3.3 :024 > next_consonant("d")
=> "f"
FWIW:
VOWELS = %w(a e i o u) | %w(a e i o u).map(&:upcase)
CONSONANTS = ((?a..?z).to_a | (?a..?z).map(&:upcase)) - VOWELS
def next_elem letter
array = VOWELS.include?(letter) ? VOWELS : CONSONANTS
array.each_cons(2) { |me, they| break they if me == letter }
end
"Felicia Torres".split(' ').reverse.map do |l|
l.split('').map(&method(:next_elem))
end.map(&:join).join(' ')
#⇒ "Vussit Gimodoe"
It sounds like your question might be better suited for https://codereview.stackexchange.com/ ?
That said - I'd recommend you look into these two methods:
Array#rotate
String#tr
Which can simplify the code to something like this:
#vowels = %w( a e i o u )
#consonants = ('a'..'z').to_a - #vowels
def alias_manager(name)
rotate_letters(name).split.reverse.map(&:capitalize).join(' ')
end
def rotate_letters(name)
name.downcase.tr(#vowels.join, #vowels.rotate.join).tr(#consonants.join, #consonants.rotate.join)
end
This method has been designed with efficiency in mind. The idea is to first create a hash that does the mapping of letters when encrypting individual words (using the form of String#gsub that employs a hash for making substitutions). This should make encryption very fast, which would be particularly effective when there there are many strings to encrypt. As explained below, another benefit of using a hash is that it make it easy create a method that decrypts encrypted strings.
Code
def create_hash(arr)
puts "arr=#{arr}"
(arr + [arr.first]).each_cons(2).with_object({}) { |(k,v),h| h[k]=v }
end
v = %w(a e i o u)
c = ("a".."z").to_a - v
subs_hash = create_hash(v).
merge(create_hash(v.map(&:upcase))).
merge(create_hash(c)).
merge(create_hash(c.map(&:upcase)))
#=> {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a",
# "A"=>"E", "E"=>"I", "I"=>"O", "O"=>"U", "U"=>"A",
# "b"=>"c", "c"=>"d", ..., "y"=>"z", "z"=>"b",
# "B"=>"C", "C"=>"D", ..., "Y"=>"Z", "Z"=>"B"}
def code(str, subs_hash)
str.split.reverse.map { |word| word.gsub(/./, subs_hash) }.join(' ')
end
Examples
code("Felicia Torres", h)
#=> "Vussit Gimodoe"
code("eenie meanie", h)
#=> "niepoi iipoi"
Decrypt encrypted string
One advantage of using a hash is that it makes writing a decode method very easy.
inverted_subs_hash = subs_hash.invert
#=> {"e"=>"a", "i"=>"e", "o"=>"i", "u"=>"o", "a"=>"u",
# "E"=>"A", "I"=>"E", "O"=>"I", "U"=>"O", "A"=>"U",
# "c"=>"b", "d"=>"c",..., "z"=>"y", "b"=>"z",
# "C"=>"B", "D"=>"C",..., "Z"=>"Y", "B"=>"Z"}
def decode(str, inverted_subs_hash)
code(str, inverted_subs_hash)
end
decode "Vussit Gimodoe", inverted_subs_hash
#=> "Felicia Torres"
Upper and lower case
If the string is first downcased, remove
merge(create_hash(v.map(&:upcase))).
and
merge(create_hash(c.map(&:upcase)))
Doing so results in decode(code(str)) != str unless we assume the first letter of each word is capitalized and all other characters are lower case, in which case we could make decode return the original string by applying (as a final step) String#capitalize to each decoded word.

How to collect user input and give feedback

I am struggling with my code to write a simple code-breaking game.
There is a hidden code:
code = ["a","b","b","c"]
My program asks for user input, then stores it in a variable.
I want to compare user input against the secret code variable and give the user feedback: 1 for a good letter in good place, 0 for good letter in wrong place, "-" for wrong letter.
I came up with something like this:
feedback = []
input.each_with_index do |v,i|
if v == code.fetch(i)
feedback << "1"
else
feedback << "-"
end
end
It works OK when it compares elements at the same index. I have no idea how I can find elements that are in the code array, but not in the same index and give feedback to the user.
For example:
code = ["a","b","b","c"]
input = ["b","b","a","z"]
feedback = ["0","1","0","-"]
This code works with the 3 examples you mentioned.
2 passes are used because the 1s must be returned before the 0s :
def give_feedback(input, code)
feedback = Array.new(input.size) { '-' }
code2 = code.dup
input.each_with_index do |letter, index|
if letter == code[index]
feedback[index] = '1'
code2[index] = nil
end
end
input.each_with_index do |letter, index|
next if feedback[index] == '1'
found = code2.index(letter)
if found
feedback[index] = '0'
code2[found] = nil
end
end
feedback
end
p give_feedback(%w(b b a z), %w(a b b c))
# ["0", "1", "0", "-"]
p give_feedback(%w(a a a a), %w(a b b c))
# ["1", "-", "-", "-"]
p give_feedback(%w(c c b a), %w(a b b c))
# ["0", "-", "1", "0"]
One more solution
code = ["a","b","b","c"]
input = ["b","b","a","z"]
feedback = input.map.with_index do |num, ind|
if code.include? num
code[ind] == num ? '1' : '0'
else
'-'
end
end
=> ['0', '1', '0', '-']
if you want define feedback before, just edit 1st variant to:
code = ["a","b","b","c"]
input = ["b","b","a","z"]
feedback = []
input.each_with_index do |num, ind|
if code.include? num
feedback << (code[ind] == num ? '1' : '0')
else
feedback << '-'
end
end
result would be the same
You can use zip and map to make it a little more functional. include? will check to see if the input is in code
code = %w(a b b c)
input = %w(b b a z)
result = code.zip(input).map do |c, i|
if c == i
'1'
elsif code.include?(i)
'0'
else
'-'
end
end
puts result.to_s
You can use the include? method to see if that character is in the list at a different index. Something like this:
input.each_with_index do |v,i|
if v == code.fetch(i)
feedback << "1"
elsif code.include?(v)
# right character, wrong spot
feedback << "0"
else
feedback << "-"
end
end
Just out of curiosity:
[code, input].map { |a| (0...a.size).zip(a).to_h }
.reduce do |e, acc|
c = acc.values.dup
acc.merge(e) do |_, v1, v2|
case (c.delete_at(c.index(v2)) rescue nil)
when v1 then "1"
when nil then "-"
else "0"
end
end
end.values

Ruby calling an array in a function [duplicate]

This question already has answers here:
Why am I getting objects printed twice?
(4 answers)
Closed 6 years ago.
Trying to call an array from a function to just assert_equal to make sure it is returning the intended string.
here is my function:
def array_mod
a = *(1..100)
a.each { |i| if i % 3 == 0 && i % 5 == 0; i = "fifteen" elsif i % 3 == 0; i = "three" elsif i % 5 == 0; i = "five" else i = i end }
end
and here is my attempt at calling it.
require "minitest/autorun"
require_relative "array_modulus.rb"
class TestArrayFunction < Minitest::Test
def test_array1
results = array_mod
assert_equal(100, results.length)
end
def test_array2
results = array_mod
assert_equal("three", results[2])
end
end
The test passes the results.length, but returns the "three" as 3, an integer.
I know I could create an array and do it like
def abc
arr = []
*(1..100) do |i|
if i % 3 == 0
i = "three"
else
i = I
end
But I was curious if I could do it with the previous way of writing it.
Sorry for any mistakes, I wrote this on my phone.
You want to to use map. Try this:
def array_mod
a = *(1..100)
a.map do |i|
if i % 3 == 0 && i % 5 == 0
"fifteen"
elsif i % 3 == 0
"three"
elsif i % 5 == 0
"five"
end
end
end
The value of a method is the last expression evaluated in the method. In your case, it is a.each {...}. This method always returns a.
Actually, it is not clear to me what you intended to do with the each block, as the only thing it does is changing the local variable i inside the block, which doesn't affect anything outside the block.
Hence, your method is equivalent to
def array_mod
(1..100).to_a
end

Ruby arrays methods and outputs

Given a number, my code should return all the even numbers between 1 and the number, and print them in the following format:
22
4444
666666
etc...
This is the code so far:
def pattern(n)
n == 1 ? "" : arr = (1..n).select {|i| i if i % 2 == 0}.each {|item| return (item.to_s * item)}
end
With any number greater than four, it will only return the following:
22
I think that this may have something to do with the return in the block. However, when using print or puts, this returns an individual array element as follows:
[2]
Ideas for a way around this so that I can achieve the desired output?
This code fixes your issue:
def pattern(n)
n == 1 ? "" : arr = (1..n).select {|i| i if i % 2 == 0}.map {|item| (item.to_s * item)}
end
Note that I'm using map instead of each, and I'm not using a return. The return meant that you didn't actually finish looping over the numbers... as soon as you got to 2 you returned from the function.
map is what you want if you want to build up a new array with the results.
EDIT
A little more cleanup:
def pattern(n)
n == 1 ? "" : (1..n).select {|i| i.even?}.map {|item| item.to_s * item}
end
The arr = is unnecessary. Your block in a select should just return true or false... you could also use just i % 2 == 0 in there, but even? happens to exist. Also, the parentheses around item.to_s * item are unnecessary.
EDIT 2
Per comments below, if you want a single string, maybe this is what you're looking for (added .join("\n")):
def pattern(n)
n == 1 ? "" : (1..n).select {|i| i.even?}.map {|item| item.to_s * item}.join("\n")
end
EDIT 3
When returning a string, you can also skip the n==1 special case, since joining an empty array will just return an empty string:
def pattern(n)
(1..n).select {|i| i.even?}.map {|item| item.to_s * item}.join("\n")
end
Your code doesn't work because it returns when it reaches the first value. See:
def pattern n
return "" if n == 1
(1..n).select { |i|
i if i % 2 == 0
}.each { |item|
return (item.to_s * item) # You are returning here!
}
end
As a suggestion, you could simplify your code to:
def pattern n
(2..n).step(2) { |n| puts n.to_s * n }
end
or --even better IMO-- you return an array with all results and let the caller decide what to do with it:
def pattern n
(2..n).step(2).map { |n| n.to_s * n }
end
Here is another way in which you can tackle the problem, by employing Integer#times:
def pattern n
(2..n).each do |i|
next if i.odd?
i.times { print i }
puts
end
end
pattern 8
#=>
# 22
# 4444
# 666666
# 88888888

Return a Boolean if an element occurs three times in a row in an array

I am trying to write a method that takes an array and returns trueif there is an element that occurs three times in a row or false if it doesn't. I can't think of the syntax. Would you use count? See the example below.
def got_three?(array)
end
got_three?([1,2,2,3,4,4,4,5,6]) would return true as 4 shows up three times in a row.
Toying with the new Ruby 2.3.0 method chunk_while:
def got_three?(array)
array.chunk_while(&:==).any?{|g| g.size >= 3}
end
With Enumerable#chunk:
def got_three?(xs)
xs.chunk(&:itself).any? { |y, ys| ys.size >= 3 }
end
Not so smart but a naive one (using a instead of array since it is long):
a.each_index.any?{|i| a[i] == a[i + 1] and a[i + 1] == a[i + 2]}
I assume you don't have any nil in the array.
An alternative which may be more performant (as per #sawa's comments)...
def got_three?(array)
(0..(array.count-2)).any?{|i|array[i] == array[1+1] && array[i] == array[i+2]}
end
Look, ma, no indices!
def ducks_in_a_row?(arr, n)
cnt = 0
last = arr.first
arr.each do |d|
if d==last
cnt += 1
return true if cnt==n
else
last = d
cnt = 1
end
end
false
end
ducks_in_a_row?([1,2,3,4,5,6,6,7,7,7], 3)
#=> true
def got_three?(array)
array.each_cons(3).map{|g|g.uniq.length == 1}.any?
end
or as #wandmaker suggests...
def got_three?(array)
array.each_cons(3).any?{|g|g.uniq.length == 1}
end
Here is my take on this problem - I tried to make it generic, hopefully efficient so that the loop terminates as soon as n-consecutive elements are found.
def got_consecutive?(array, n = 3)
case array.size
when 0...n
return false
when n
return array.uniq.size == n
else
array[n..-1].each_with_object(array[0...n]) do |i, t|
(t.uniq.size == 1 ? (break t) : (t << i).shift)
end.uniq.size == 1
end
end
p got_consecutive?([])
#=> false
p got_consecutive?([1,2])
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3], 3)
#=> true
p got_consecutive?([1,2,2,3,2,3,3,3], 4)
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3,3,3,4,4,4,4], 5)
#=> true
The code takes care of border cases first such as when array did not have n elements in which case answer is obviously false, and another one being when the array had only n elements - in which case just a uniqueness check would suffice.
For cases where array size is greater than n, the code uses Enumerable#each_with_object with the initial object being an array of n elements from the array - this array is used also as temporary work area to track n consecutive elements and perform a check whether all those elements are same or not.

Resources