Trouble with classes in ruby and codewars - ruby

so i am trying a kata on Codewars: Flexible Card Game
http://www.codewars.com/kata/5436fdf34e3d6cb156000350/train/ruby
the code I have written has passed most tests but trips up at the end giving this:
#draw
chooses cards from the end
Test Passed: Value == [[:clubs, 13]]
removes cards from the deck
Test Passed: Value == 51
returns the cards that were drawn
Test Passed: Value == 1
Expected [:clubs, 13] to be a Card
chooses cards from the end
Test Passed: Value == [[:clubs, 12], [:clubs, 13]]
removes cards from the deck
Test Passed: Value == 50
returns the cards that were drawn
Test Passed: Value == 2
Expected [:clubs, 12] to be a Card
Expected [:clubs, 13] to be a Card
What I don't understand is that when the test calls the method draw it seems to expect to different returns from the same method. I'm sure it's something I've done wrong but I cant see it. Any help would be great. Here is my code:
class Card
include Comparable
attr_accessor :suit, :rank
def initialize(suit, rank)
#suit = suit
#rank = rank
end
def <=> (another_card)
if self.rank < another_card.rank
-1
elsif self.rank > another_card.rank
1
else
0
end
end
def face_card?
#rank > 10 ? true : false
end
def to_s
#rank_hash = {13 => "King", 12 => "Queen", 11 => "Jack", 10 => "10", 9 => "9", 8 => "8", 7 => "7", 6 => "6", 5 => "5", 4 => "4", 3 => "3", 2 => "2", 1 => "Ace"}
#suit_hash = {:clubs => "Clubs", :spades => "Spades", :hearts => "Hearts", :diamonds => "Diamonds"}
"#{#rank_hash[#rank]} of #{#suit_hash[#suit]}"
end
end
class Deck < Card
attr_accessor :cards
def initialize
#rank_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
#suit_array = [:hearts, :diamonds, :spades, :clubs]
#cards = #suit_array.product(#rank_array)
end
def count
#cards.size
end
def shuffle
#cards.shuffle!
end
def draw(n=1)
#cards.pop(n)
end
end

Issue was with the initialize method. Below fixed the issue.
def initialize
#cards = []
#rank_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
#suit_array = [:hearts, :diamonds, :spades, :clubs]
#suit_array.each do |suit|
#rank_array.each do |rank|
each_card = Card.new(suit, rank)
#cards << each_card
end
end
end

Related

Turning several Enumerables into one

Is there a way to get several Enumerable objects to appear as a single Enumerable without flattening it into an Array? Currently I've written a class like so, but I feel there must be a built-in solution.
class Enumerables
include Enumerable
def initialize
#enums = []
end
def <<(enum)
#enums << enum
end
def each(&block)
if block_given?
#enums.each { |enum|
puts "Enumerating #{enum}"
enum.each(&block)
}
else
to_enum(:each)
end
end
end
enums = Enumerables.new
enums << 1.upto(3)
enums << 5.upto(8)
enums.each { |s| puts s }
As a simple example, it needs to be able to accept an infinite enumerator like so.
inf = Enumerator.new { |y| a = 1; loop { y << a; a +=1 } };
Well, it might be done with standard library using Enumerator. The advantage of this approach would be it returns the real enumerator, that might be mapped, reduced etc.
MULTI_ENUM = lambda do |*input|
# dup is needed here to prevent
# a mutation of inputs when given
# as a splatted param
# (due to `input.shift` below)
input = input.dup.map(&:to_enum)
Enumerator.new do |yielder|
loop do
# check if the `next` is presented
# and mutate the input swiping out
# the first (already iterated) elem
input.first.peek rescue input.shift
# stop iteration if there is no input left
raise StopIteration if input.empty?
# extract the next element from
# the currently iterated enum and
# append it to our new Enumerator
yielder << input.first.next
end
end
end
MULTI_ENUM.(1..3, 4.upto(5), [6, 7]).
map { |e| e ** 2 }
#⇒ [1, 4, 9, 16, 25, 36, 49]
After all. Use Enumerable::Lazy#flat_map with .each.lazy on elements:
inf = Enumerator.new { |y| a = 1; loop { y << a; a += 1 } }
[(1..3).to_a, inf].lazy.flat_map { |e| e.each.lazy }.take(10).force
#⇒ [1, 2, 3, 1, 2, 3, 4, 5, 6, 7]
I ended up with this solution, maybe is close to what you already tried:
def enumerate(*enum)
enum.each_with_object([]) { |e, arr| arr << e.to_a }.flatten
end
enumerate( 1..3, 5.upto(8), 3.times, 'a'..'c' ).each { |e| p e }
# => 1, 2, 3, 5, 6, 7, 8, 0, 1, 2, "a", "b", "c"
Or (same mechanics):
def enumerate(*enum)
enum.flat_map { |e| e.to_a }
end

How to convert a string of values with a range to an array in Ruby

I'm trying to parse a string of numbers and ranges joined by ",", and convert it to a numerical array. I have this as input: "1,3,6-8,5", and would like to have an array like this: [1,3,5,6,7,8].
I can only do it without the range, like this:
"12,2,6".split(",").map { |s| s.to_i }.sort #=> [2, 6, 12]
With a range, I cannot do it:
a = "12,3-5,2,6"
b = a.gsub(/-/, "..") #=> "12,3..5,2,6"
c = b.split(",") #=> ["12", "3..5", "2", "6"]
d = c.sort_by(&:to_i) #=> ["2", "3..5", "6", "12"]
e = d.split(",").map { |s| s.to_i } #>> Error
How can I do this?
I was also thinking to use the splat operator in map, but splat doesn't accept strings like [*(3..5)].
"12,3-5,2,6".
gsub(/(\d+)-(\d+)/) { ($1..$2).to_a.join(',') }.
split(',').
map(&:to_i)
#⇒ [12, 3, 4, 5, 2, 6]
"1,3,6-8,5".split(',').map do |str|
if matched = str.match(/(\d+)\-(\d+)/)
(matched[1].to_i..matched[2].to_i).to_a
else
str.to_i
end
end.flatten
or
"1,3,6-8,5".split(',').each_with_object([]) do |str, output|
if matched = str.match(/(\d+)\-(\d+)/)
output.concat (matched[1].to_i..matched[2].to_i).to_a
else
output << str.to_i
end
end
or strict
RANGE_PATTERN = /\A(\d+)\-(\d+)\z/
INT_PATTERN = /\A\d+\z/
"1,3,6-8,5".split(',').each_with_object([]) do |str, output|
if matched = str.match(RANGE_PATTERN)
output.concat (matched[1].to_i..matched[2].to_i).to_a
elsif str.match(INT_PATTERN)
output << str.to_i
else
raise 'Wrong format given'
end
end
"1,3,6-8,5".split(',').flat_map do |s|
if s.include?('-')
f,l = s.split('-').map(&:to_i)
(f..l).to_a
else
s.to_i
end
end.sort
#=> [1, 3, 5, 6, 7, 8]
"1,3,6-8,5"
.scan(/(\d+)\-(\d+)|(\d+)/)
.flat_map{|low, high, num| num&.to_i || (low.to_i..high.to_i).to_a}
#=> [1, 3, 6, 7, 8, 5]

Ruby How to convert string to integer without .to_i

Is there a way to output an integer given a string containing numbers between 0 and 9. For example, input is "219", output would be 219, and you can't use .to_i
You can use Kernel::Integer:
Integer("219")
#=> 219
Integer("21cat9")
# ArgumentError: invalid value for Integer(): "21cat9"
Sometimes this method is used as follows:
def convert_to_i(str)
begin
Integer(str)
rescue ArgumentError
nil
end
end
convert_to_i("219")
#=> 219
convert_to_i("21cat9")
#=> nil
convert_to_i("1_234")
#=> 1234
convert_to_i(" 12 ")
#=> 12
convert_to_i("0b11011") # binary representation
#=> 27
convert_to_i("054") # octal representation
#=> 44
convert_to_i("0xC") # hexidecimal representation
#=> 12
Some use an "inline rescue" (though it is less selective, as it rescues all exceptions):
def convert_to_i(str)
Integer(str) rescue nil
end
There are similar Kernel methods to convert a string to a float or rational.
def str_to_int(string)
digit_hash = {"0" => 0, "1" => 1, "2" => 2, "3" => 3, "4" => 4, "5" => 5, "6" => 6, "7" => 7, "8" => 8, "9" => 9}
total = 0
num_array = string.split("").reverse
num_array.length.times do |i|
num_value = digit_hash[num_array[i]]
num_value_base_ten = num_value * (10**i)
total += num_value_base_ten
end
return total
end
puts str_to_int("119") # => 119

Ruby: reuse value in a block without assigning it to variable (write object method on the fly)

There are several situations where I'd like to apply a block to a certain value and use the value inside this block, to use the enumerator coding style to every element.
If such method would be called decompose, it would look like:
result = [3, 4, 7, 8].decompose{ |array| array[2] + array[3] } # result = 15
# OR
result = {:key1 => 'value', :key2 => true}.decompose{ |hash| hash[:key1] if hash[:key2] } # result = 'value'
# OR
[min, max] = [3, 4, 7, 8].decompose{ |array| [array.min, array.max] } # [min, max] = [3, 8]
# OR
result = 100.decompose{ |int| (int - 1) * (int + 1) / (int * int) } # result = 1
# OR
result = 'Paris'.decompose{ |str| str.replace('a', '') + str[0] } # result = 'PrisP'
The method simply yields self to the block, returning the block's result. I don't think it exists, but you can implement it yourself:
class Object
def decompose
yield self
end
end
[3, 4, 7, 8].decompose{ |array| array[2] + array[3] }
#=> 15
{:key1 => 'value', :key2 => true}.decompose{ |hash| hash[:key1] if hash[:key2] }
#=> "value"
[3, 4, 7, 8].decompose{ |array| [array.min, array.max] }
#=> [3, 8]
It actually exists (I could not believe it didn't).
It is called BasicObject#instance_eval. Here's the doc: http://apidock.com/ruby/BasicObject/instance_eval
Available since Ruby 1.9 as this post explains: What's the difference between Object and BasicObject in Ruby?

ruby - group by repeating key of multiple hashes

I have the following array
t = [
{nil => 1, 10 => 2, 16 => 4, 5=> 10},
{nil => 9, 5 => 2, 17 => 3, 10 => 2},
{10 => 4, 5 => 9, 17 => 1}
]
how can I get this as result?
{nil => [1,9,0],10 => [2,2,4], 16 => [4,0,0], 5 => [10,2,9], 17=>[0,3,1]}
I've seen that I can use something like this
t.group_by{|h| h['key']}
but I'm not sure if I can put a regexp inside the brackets
Thanks in advance
Javier
EDIT:
Is just want to group by each key of each hash inside the array, if the key is not present then the value is 0 for that hash
How about this one for illegibility:
t = [
{nil => 1, 10 => 2, 16 => 4, 5=> 10},
{nil => 9, 5 => 2, 17 => 3, 10 => 2},
{10 => 4, 5 => 9, 17 => 1}
]
# Create hash of possible keys
keys = t.reduce({}) { |m, h| h.each_key { |k| m[k] = [] }; m }
# Iterate through array, for each hash, for each key, append the
# value if key is in hash or zero otherwise
t.reduce(keys) { |m, h| m.each_key { |k| m[k] << (h[k] || 0) }; m }
puts keys
#=> {nil=>[1, 9, 0], 10=>[2, 2, 4], 16=>[4, 0, 0], 5=>[10, 2, 9], 17=>[0, 3, 1]}
Not the most elegant code I've ever written, but it does the job and is easy to understand:
def jqq(a)
keys = []
result = {}
a.each do |h|
keys += h.keys
end
keys.uniq.each do |key|
result[key] = []
a.each do |h|
h.default = 0
result[key] << h[key]
end
end
result
end
t = [
{nil => 1, 10 => 2, 16 => 4, 5=> 10},
{nil => 9, 5 => 2, 17 => 3, 10 => 2},
{10 => 4, 5 => 9, 17 => 1}
]
puts jqq(t)
# {nil=>[1, 9, 0], 10=>[2, 2, 4], 16=>[4, 0, 0], 5=>[10, 2, 9], 17=>[0, 3, 1]}
I do not think there is any any function available
Just gave a try with hash
def do_my_work(data)
hash = {}
#get all keys first
arr.map{|m| m.keys}.flatten.uniq.each {|a| hash[a]=[]}
# Now iterate and fill the values
arr.each do |elm|
hash.each do |k,v|
hash[k] << (elm[k].nil? ? 0 : elm[k])
end
end
end
hash = do_my_work(t)
puts hash
# => {nil=>[1, 9, 0], 10=>[2, 2, 4], 16=>[4, 0, 0], 5=>[10, 2, 9], 17=>[0, 3, 1]}

Resources