I have been making a chess game and I need some help with hashes. Specifically how do I automatically name a hash table symbol using an iterator 'i'
8.times do |i = 0, x = 0|
i += 1
x += 1
pawnHash[:P] = "P#{i}",Pawn.new(x,2,"P#{i}","black")
end
puts pawnHash
the symbol should look like this:
:P1. But is seems impossible to name a hash using the variable 'i'
The full set of 8 symbols should look like this: :P1, :P2, :P3 ... etc.
I tried doing :P + i when declaring the key/value pair, but I got a syntax error due to the '+' sign.
Are you trying to make the key a symbol?
You can do hash["P#{i}".to_sym]
2.0.0-p247 :016 > i = 2
=> 2
2.0.0-p247 :017 > h = {}
=> {}
2.0.0-p247 :018 > h["P#{i}".to_sym] = "value"
=> "value"
2.0.0-p247 :019 > h
=> {:P2=>"value"}
2.0.0-p247 :020 > h.keys.first.class
=> Symbol
Or you can do :"P#{i}"
You can simplify your loop and make it more Ruby-like:
pawn_hash = {}
8.times { |i| pawn_hash["P#{ i + 1 }".to_sym] = "P#{ i + 1}" }
pawn_hash
# => {:P1=>"P1",
# [...]
# :P8=>"P8"}
You could avoid using i + 1 by assigning it to an intermediate variable if you want to play the DRY game:
pawn_hash = {}
8.times do |i|
c = i + 1
pawn_hash["P#{ c }".to_sym] = "P#{ c }"
end
pawn_hash
# => {:P1=>"P1",
# [...]
# :P8=>"P8"}
Or, use a different loop:
pawn_hash = {}
1.upto(8) { |i| pawn_hash["P#{ i }".to_sym] = "P#{ i }" }
pawn_hash
# => {:P1=>"P1",
# [...]
# :P8=>"P8"}
In Ruby we use snake_case, instead of camelCase, for variable and method names. Classes and Modules get camelCase.
Also, meditate on these:
pawn_hash = 8.times.map { |i| ["P#{ i + 1 }".to_sym, "P#{ i + 1}"] }.to_h
# => {:P1=>"P1",
# :P2=>"P2",
# :P3=>"P3",
# :P4=>"P4",
# :P5=>"P5",
# :P6=>"P6",
# :P7=>"P7",
# :P8=>"P8"}
pawn_hash = Hash[8.times.map { |i| ["P#{ i + 1 }".to_sym, "P#{ i + 1}"] }]
# => {:P1=>"P1",
# :P2=>"P2",
# :P3=>"P3",
# :P4=>"P4",
# :P5=>"P5",
# :P6=>"P6",
# :P7=>"P7",
# :P8=>"P8"}
# :P8=>"P8"}
It's not necessary to loop and assign to the hash. Instead, it's very Ruby-like to do it all in one pass. The times method is an iterator. map can iterate over that and will return the block values for each iteration. to_h is a more modern way in Ruby to convert an array to a hash, just as is using Hash[...].
Related
I need to count the number of peaches and eggplants in a string and say which appears more. I tried this:
def counting(eggplant_peaches)
eggplants_counting = 0
peaches_counting = 0
(0..eggplant_peaches.length).each do |i|
if eggplant_peaches[i] == π
eggplants_counting = eggplants_counting + 1
elsif eggplant_peaches[i] == π
peaches_counting = peaches_counting + 1
end
end
if eggplants_counting > peaches_counting
puts βMore πβ
elsif peaches_counting > eggplants_counting
puts βMore πβ
end
end
I get an error:
undefined local variable or method 'π' for main:Object
How can I count and make my code littler [sic]?
Your eggplants and peaches need to be enclosed in quotes to be proper strings,
if eggplant_peaches[i] == "π"
Now as you asked how to make your code shorter, you could do this:
def counting(array)
winner = array.group_by(&:itself).sort_by {|k,v| v.size }.last.first
# The steps here are:
# %w[π π π].group_by(&:itself)
# => {"π"=>["π"], "π"=>["π", "π"]}
# .sort_by { |k,v| v.size}
# => [["π", ["π"]], ["π", ["π", "π"]]]
# .last
# => ["π", ["π", "π"]]
# .first
# => "π"
puts "More #{winner}"
end
counting(%w[π π π])
=> More π
As a bonus, the above code also works for bananas:
counting(%w[π π π π π π])
=> More π
a bit faster approach (frequency counting):
array.each.with_object(Hash.new(0)) do |i, res|
res[i] += 1
end.max_by(&:last).first
though you can also make it fast enough using :max_by in Marcin KoΕodziej's decision:
array.group_by(&:itself)
.max_by { |k, v| v.size }
.first
I seem to be getting something elementary wrong when trying to set and or access these variables in my Quote instance when i call Quote#scrape_quote. I can see that the values are scraped and saved to the scraped_values hash just fine, I just can't access the variables, when I call quote.lives for e.g. I get nil.
What am i getting wrong here?
quote.rb
class Quote < ApplicationRecord
require 'watir'
attr_accessor :lives, :benefit, :payment
def scrape_quote
rows = #browser.trs
quote_rows = rows[1..8]
scraped_values = {}
quote_rows.each do |row|
scraped_values[row.tds[0].text] = row.tds[1].text
end
#lives = scraped_values[0]
#benefit = scraped_values[1]
#payment = scraped_values[2]
puts scraped_values
end
end
scraped_values is a hash not an array. You are trying to access it as if it were an array.
Use whatever is in row.tds[0].text to reference the hash:
h = {a:1,b:2}
h[:a]
=> 1
The general case
If you just want the values of the hash sequentially assigned to member variables, you can use parallel assignment from the hash#values return value like this:
2.4.1 :001 > h = {}
=> {}
2.4.1 :002 > h[:one] = 1
=> 1
2.4.1 :003 > h[:two] = 2
=> 2
2.4.1 :004 > h[:three] = 3
=> 3
2.4.1 :005 > #one, #two, #three = h.values
=> [1, 2, 3]
2.4.1 :006 > #one
=> 1
2.4.1 :007 > #two
=> 2
2.4.1 :008 > #three
=> 3
2.4.1 :009 >
Specific application
The specific code in your case would turn into:
class Quote < ApplicationRecord
require 'watir'
attr_accessor :lives, :benefit, :payment
def scrape_quote
rows = #browser.trs
quote_rows = rows[1..8]
scraped_values = {}
quote_rows.each do |row|
scraped_values[row.tds[0].text] = row.tds[1].text
end
#lives, #benefit, #payment = scraped_values.values
puts scraped_values
end
end
The idiomatic way to build the hash would be to use map instead of each and do not use upfront declarations of local variables.
scraped_values = quote_rows.map do |row|
[row.tds[0].text, row.tds[1].text]
end.to_h
Instead of scraped_values[0] you need something like this: scraped_values[scraped_values.keys[0]], because scraped_values isn't array and 0, 1, 2 is like any other missing key, so hash returns nil.
I would like to make a function so each time it is called it return a value that is incremented by 1 starting from 1.
I guess it could be made with a global variable, but not a pretty solution.
I'd probably do it this way:
class EverreadyBunnyCounter
def initialize
#counter = 0
end
def current
#counter
end
def next
#counter += 1
end
end
foo = EverreadyBunnyCounter.new
bar = EverreadyBunnyCounter.new
foo.next # => 1
bar.next # => 1
foo.next # => 2
bar.current # => 1
The current method isn't necessary but it's sometimes convenient to be able to peek at the current value without forcing it to increment.
Alternately, this might do it:
MAX_INT = (2**(0.size * 8 -2) -1)
counter = (1..MAX_INT).to_enum # => #<Enumerator: 1..4611686018427387903:each>
foo = counter.dup
bar = counter.dup
foo.next # => 1
foo.next # => 2
bar.next # => 1
bar.next # => 2
foo.next # => 3
Defining MAX_INT this way comes from "Ruby max integer". The downside is that you'll eventually run out of values because of the range being used to create the Enumerator, where the previous version using the EverreadyBunnyCounter class will keep on going.
Changing MAX_INT to Float::INFINITY would be a way to fix that:
counter = (1..Float::INFINITY).to_enum # => #<Enumerator: 1..Infinity:each>
foo = counter.dup
bar = counter.dup
foo.next # => 1
foo.next # => 2
bar.next # => 1
bar.next # => 2
foo.next # => 3
The Enumerator documentation has more information.
Something like this?
def incr
#n ||= 0
#n += 1
end
incr
#=> 1
incr
#=> 2
When compare two version strings, to_f does not work well:
> "1.5.8".to_f > "1.5.7".to_f
=> false
string comparison is better, but not always correct:
> "1.5.8" > "1.5.7"
=> true
> "1.5.8" > "1.5.9"
=> false
> "1.5.8" > "1.5.10" # oops!
=> true
How to compare version strings properly?
An idea: create a Object#compare_by method that behaves like compare (aka the spaceship operator Object#<=>) but takes a custom block:
class Object
def compare_by(other)
yield(self) <=> yield(other)
end
end
>> "1.5.2".compare_by("1.5.7") { |s| s.split(".").map(&:to_i) }
#=> -1
You can also take a more specific approach still based on the compare method:
class String
def compare_by_fields(other, fieldsep = ".")
cmp = proc { |s| s.split(fieldsep).map(&:to_i) }
cmp.call(self) <=> cmp.call(other)
end
end
>> "1.5.8".compare_by_fields("1.5.8")
#=> 0
Personally I'd probably just use the Versionomy gem, no need to reinvent this specific wheel IMHO.
Example:
require 'versionomy'
v1 = Versionomy.parse("1.5.8")
v2 = Versionomy.parse("1.5.10")
v2 > v1
#=> true
First start off by splitting the different parts of the versions:
v1 = "1.5.8"
v2 = "1.5.7"
v1_arr = v1.split(".")
=> ["1", "5", "8"]
v2_arr = v2.split(".")
=> ["1", "5", "7"]
v1_arr.size.times do |index|
if v1_arr[index] != v2_arr[index]
# compare the values at the given index. Don't forget to use to_i!
break
end
end
>> a = 5
=> 5
>> b = a
=> 5
>> b = 4
=> 4
>> a
=> 5
how can I set 'b' to actually be 'a' so that in the example, the variable a will become four as well. thanks.
class Ref
def initialize val
#val = val
end
attr_accessor :val
def to_s
#val.to_s
end
end
a = Ref.new(4)
b = a
puts a #=> 4
puts b #=> 4
a.val = 5
puts a #=> 5
puts b #=> 5
When you do b = a, b points to the same object as a (they have the same object_id).
When you do a = some_other_thing, a will point to another object, while b remains unchanged.
For Fixnum, nil, true and false, you cannot change the value without changing the object_id. However, you can change other objects (strings, arrays, hashes, etc.) without changing object_id, since you don't use the assignment (=).
Example with strings:
a = 'abcd'
b = a
puts a #=> abcd
puts b #=> abcd
a.upcase! # changing a
puts a #=> ABCD
puts b #=> ABCD
a = a.downcase # assigning a
puts a #=> abcd
puts b #=> ABCD
Example with arrays:
a = [1]
b = a
p a #=> [1]
p b #=> [1]
a << 2 # changing a
p a #=> [1, 2]
p b #=> [1, 2]
a += [3] # assigning a
p a #=> [1, 2, 3]
p b #=> [1, 2]
You can't. Variables hold references to values, not references to other variables.
Here's what your example code is doing:
a = 5 # Assign the value 5 to the variable named "a".
b = a # Assign the value in the variable "a" (5) to the variable "b".
b = 4 # Assign the value 4 to the variable named "b".
a # Retrieve the value stored in the variable named "a" (5).
See this article for a more in-depth discussion of the topic: pass by reference or pass by value.
As has been noted the syntax you are using can not be done. Just throwing this out there though you could make a wrapper class it depends what you actually want to do
ruby-1.8.7-p334 :007 > class Wrapper
ruby-1.8.7-p334 :008?> attr_accessor :number
ruby-1.8.7-p334 :009?> def initialize(number)
ruby-1.8.7-p334 :010?> #number = number
ruby-1.8.7-p334 :011?> end
ruby-1.8.7-p334 :012?> end
=> nil
ruby-1.8.7-p334 :013 > a = Wrapper.new(4)
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :014 > b = a
=> #<Wrapper:0x100336db8 #number=4>
ruby-1.8.7-p334 :015 > a.number = 6
=> 6
ruby-1.8.7-p334 :016 > a
=> #<Wrapper:0x100336db8 #number=6>
ruby-1.8.7-p334 :017 > b
=> #<Wrapper:0x100336db8 #number=6>
You can use arrays:
a = [5]
b = a
b[0] = 4
puts a[0] #=> 4
This idea is based on this answer.
Just for the sake of reference.
>> a = 5
=> 5
>> a.object_id
=> 11
>> b = a
=> 5
>> b.object_id
=> 11
>> b = 4
=> 4
>> b.object_id
=> 9
>> a.object_id
=> 11
# We did change the Fixnum b Object.
>> Fixnum.superclass
=> Integer
>> Integer.superclass
=> Numeric
>> Numeric.superclass
=> Object
>> Object.superclass
=> BasicObject
>> BasicObject.superclass
=> nil
I hope this gives us all a little better understanding about objects in Ruby.
One option in cases where you feel you would like to have direct pointer operations is to use the replace method of Hashes, Arrays & Strings.
this is useful for when you would like to have a method return a variable that a proc the method sets up will change at a later date, and don't want the annoyance of using a wrapper object.
example:
def hash_that_will_change_later
params = {}
some_resource.on_change do
params.replace {i: 'got changed'}
end
params
end
a = hash_that_will_change_later
=> {}
some_resource.trigger_change!
a
{i: 'got changed'}
It's probably better generally to use explicit object wrappers for such cases, but this pattern is useful for building specs/tests of asynchronous stuff.
I'm no Ruby expert. But for a technically crazy kluge...that would only work if you felt like going through eval every time you worked with a variable:
>> a = 5
=> 5
>> b = :a
=> :a
>> eval "#{b} = 4"
=> 4
>> eval "#{a}"
=> 4
>> eval "#{b}"
=> 4
Note that a direct usage of b will still give you :a and you can't use it in expressions that aren't in eval:
>> b
=> :a
>> b + 1
NoMethodError: undefined method `+' for :a:Symbol
...and there are certainly a ton of caveats. Such as that you'd have to capture the binding and pass it around in more complex scenarios...
'pass parameter by reference' in Ruby?
#Paul.s has an answer for if you can change the point of declaration to be a wrapper object, but if you can only control the point of reference then here's a BasicReference class I tried:
class BasicReference
def initialize(r,b)
#r = r
#b = b
#val = eval "#{#r}", #b
end
def val=(rhs)
#val = eval "#{#r} = #{rhs}", #b
end
def val
#val
end
end
a = 5
puts "Before basic reference"
puts " the value of a is #{a}"
b = BasicReference.new(:a, binding)
b.val = 4
puts "After b.val = 4"
puts " the value of a is #{a}"
puts " the value of b.val is #{b.val}"
This outputs:
Before basic reference
the value of a is 5
After b.val = 4
the value of a is 4
the value of b.val is 4