I'm trying to shorten my Ruby code.
def count_palindromes_in_an(array)
palindromes = 0
array.each { |word| palindromes += 1 if word == word.reverse }
return palindromes
end
so that palindromes is instantiated within the block executed by the each method. Something along the lines of;
def count_palindromes_in_an(array)
array.each { |word| (palindromes != nil ? palindromes += 1 : palindromes = 1) if word == word.reverse }
return palindromes
end
However this returns an error of undefined method 'palindromes'. Any tips gratefully received.
This is not going to work because a block creates a new scope. Variables defined inside the block are isolated from the outer scope.
[1].each do
palindromes = 1
local_variables #=> [:palindromes]
end
local_variables #=> []
To count array elements, use Array#count:
array.count { |word| word == word.reverse }
You could even add a palindrome? method to String:
class String
def palindrome?
self == reverse
end
end
And shorten your code to:
array.count(&:palindrome?)
Related
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).
Need to compare numbers in an array to 'winning' number. But I have to see if 3 out of 4 match. eg "1234" is my number and winning=["4356","8312","4820","7623"] . In this case "8312" should alert a win because they have 1 2&3 in common. I have to define the numbers in a unit test, then write a function in a separate file then pass that function back into the unit test. Any help would be greatly appreciated. I already wrote a function and test comparing for an exact match and dont have any idea on what next step to take.
function_file
def match(my_num,arr)
matches = []
arr.each_with_index do |v,i|
if my_num == v
matches << my_num
end
end
matches
end
test_file
require "minitest/autorun"
require_relative "close_but_no_func.rb"
class TestWinningNumbers < Minitest::Test
def test_1_equals_1
assert_equal(10-5, 3+2)
end
def test_winning_num
my_num = "1134"
arr=["6028","2088","3058","3476","8740","1134"]
assert_equal(["1134"], match(my_num, arr))
end
end
So let us divide this problem into two seperate problems.
You want a function that counts the number of matching characters.
Then you want to collect these strings together, if they have enough matching characters.
You could for example write a function that checks how many characters of the both strings match.
def count_matching_chars(str1,str2)
# counts how many characters of str1 appear in str2
matching_chars_count = 0
str1.each_char do |char|
matching_chars_count += 1 if str2.include?(char)
end
matching_chars_count
end
puts count_matching_chars("1234", "1134") => 3
puts count_matching_chars("1111", "1134") => 4
puts count_matching_chars("1234", "1111") => 1
This one here ignores the positioning, it just checks, how many of the characters of str1 match one character of str2.
Now you can easily collect these numbers in an array.
def matches(my_num, arr)
result = []
arr.each do |num|
result << arr if count_matching_chars(my_num,num) >= 3
end
result
end
You can write both functions in a more compact way, by using enumerator functions like count and select:
def count_matching_chars(str1,str2)
str1.each_char.count do |char|
str2.include?(char)
end
end
def matches(my_num, arr)
arr.select do |num|
return true if count_matching_chars(num,my_num) >= 3
end
end
Or you then combine everything into one function
def matches(my_num, arr)
arr.select do |num|
true if my_num.each_char.count { |char| num.include?(char)} >= 3
end
end
Now if you just want to check, if it is a winning number. You just return true as soon as you find a match:
def winning_number?(my_num, arr)
arr.select do |num|
return true if my_num.each_char.count { |char| num.include?(char)} >= 3
end
end
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
I'm trying to solve this exercise from Ruby Monk website, which says:
Try implementing a method called occurrences that accepts a string
argument and uses inject to build a Hash. The keys of this hash should
be unique words from that string. The value of those keys should be
the number of times this word appears in that string.
I've tried to do it like this:
def occurrences(str)
str.split.inject(Hash.new(0)) { |a, i| a[i] += 1 }
end
But I always get this error:
TypeError: no implicit conversion of String into Integer
Meanwhile, the solution for this one is quite the same (I think):
def occurrences(str)
str.scan(/\w+/).inject(Hash.new(0)) do |build, word|
build[word.downcase] +=1
build
end
end
Okay so your issue is that you are not returning the correct object from the block. (In your case a Hash)
#inject works like this
[a,b]
^ -> evaluate block
| |
-------return-------- V
In your solution this is what is happening
def occurrences(str)
str.split.inject(Hash.new(0)) { |a, i| a[i] += 1 }
end
#first pass a = Hash.new(0) and i = word
#a['word'] = 0 + 1
#=> 1
#second pass uses the result from the first as `a` so `a` is now an integer (1).
#So instead of calling Hash#[] it is actually calling FixNum#[]
#which requires an integer as this is a BitReference in FixNum.Thus the `TypeError`
Simple fix
def occurrences(str)
str.split.inject(Hash.new(0)) { |a, i| a[i] += 1; a }
end
#first pass a = Hash.new(0) and i = word
#a['word'] = 0 + 1; a
#=> {"word" => 1}
Now the block returns the Hash to be passed to a again. As you can see the solution returns the object build at the end of the block thus the solution works.
Don't understand why #nums.pop won't work in the value method. It seems to tell me that it can't do that for nil, but if I just say #nums, it shows that there is indeed something in the array. So then why can't I pop it out?
class RPNCalculator
def initialize
#value = value
nums ||= []
#nums = nums
end
def push(num)
#nums << num
end
def plus
if #nums[-2] == nil || #nums[-1] == nil
raise "calculator is empty"
else
#value = #nums.pop + #nums.pop
#nums.push(#value)
end
end
def minus
if #nums[-2] == nil || #nums[-1] == nil
raise "calculator is empty"
else
#value = #nums[-2] - #nums[-1]
#nums.pop(2)
#nums.push(#value)
end
end
def divide
if #nums[-2] == nil || #nums[-1] == nil
raise "calculator is empty"
else
#value = #nums[-2].to_f / #nums[-1].to_f
#nums.pop(2)
#nums.push(#value)
end
end
def times
if #nums[-2] == nil || #nums[-1] == nil
raise "calculator is empty"
else
#value = #nums.pop.to_f * #nums.pop.to_f
#nums.push(#value)
end
end
def value
#nums #Don't understand why #nums.pop won't work here
end
def tokens(str)
str.split(" ").map { |char| (char.match(/\d/) ? char.to_i : char.to_sym)}
end
def evaluate(str)
tokens(str).each do |x|
if x == ":-"
minus
elsif x == ":+"
plus
elsif x == ":/"
divide
elsif x ==":*"
times
else
push(x)
end
end
value
end
end
Error relates to the following part of a spec:
it "adds two numbers" do
calculator.push(2)
calculator.push(3)
calculator.plus
calculator.value.should == 5
end
Error says either:
Failure/Error: calculator.value.should == 5
expected: 5
got: [5] <using ==>
OR if .pop is used
Failure/Error: #calculator = RPNCalculator.new
NoMethodError:
undefined method 'pop' for nil:NilClass
Your initialize method assigning #value = value calls the function at def value which returns #nums which has not yet been created in initialize since #nums is created afterwards with nums ||= []; #nums = nums therefore it's nil. This is why .pop won't work.
You've created #nums as an array with nums ||= [] and you're using it with push and pop so why are you checking for the value with value.should == 5 (Integer) when calling value returns an (Array). You would need to write it like value.first.should == 5 or value[0].should == 5 ... otherwise you should change value to return just the element you want
def value
#nums.pop # or #nums[0], or #nums.first or #nums.last however you plan on using it
end
The problem is #value = value in your initialize method. Fix that then you can add the .pop in value.
EDIT
Also your evaluation is calling methods before you've populated #nums with the values. Then the methods "raise" errors. You can't call minus after only one value has been pushed to #nums.
Here's how I would do the flow for splitting the string
# Multiplication and Division need to happen before addition and subtraction
mylist = "1+3*7".split(/([+|-])/)
=> ["1", "+", "3*7"]
# Compute multiplication and division
mylist = mylist.map {|x| !!(x =~ /[*|\/]/) ? eval(x) : x }
=> ["1", "+", 21]
# Do the rest of the addition
eval mylist.join
=> 22
I realize this isn't exactly how you're going about solving this... but I think splitting by order of mathematical sequence will be the right way to go. So first evaluate everything between (), then only multiplication and division, then all addition and subtraction.
EDIT I just looked into what a RPN Calculator is. So don't mind my splitting recommendation as it doesn't apply.