Rails 4 Undefined local variable - ruby-on-rails

I have a model method which has a local variable defined before an .each block. The block then iterates over an Array and increases the count based on whether something is true or not. The variable in question is true_count. I am getting an "undefined local variable" error for true_count that I cannot figure out. I am also relatively new to Rails and coding.
project.rb:
def displaycount
d = [self.backlog_display, self.committed_display, self.qa_display, self.started_display, self.completed_display, self.accepted_display]
true_count == 0
d.each do |d|
if d == true
true_count += 1
end
end
end
What is causing this error?

You're doing a comparison rather than a definition with your variable...
true_count == 0 needs to be true_count = 0

The problem is here in this line
true_count == 0
It should be true_count = 0
Why?
== is used for comparison,for example
if a == 0
puts 'a is zero'
else
puts 'a is not Zero'
end
And = is just used to assign a value ta variable.For example
a=0 #Here is defined as Zero
Hope it Helped!

Change this line from:
true_count == 0
to:
true_count = 0
You need to assign the variable, rather than check whether or not it's equal to zero.

def display_count
[ backlog_display,
committed_display,
qa_display,
started_display,
completed_display,
accepted_display].inject(0) {|result,display| result + ( display ? 1 : 0 ) }
end

Related

How to Call a Table from a Variable in Lua?

I'm creating a program for a turtle in ComputerCraft. The program is going to make the turtle control a warehouse of storage for my items in the game. It will check what item I put in, then it will figure out the chest's location, go there, and dump it in. I am storing the locations of each chest in a table. For example:
cobblestone = {2,0,1}
That tells the turtle that the cobblestone chest is stored at position x=2 y=0 and z=1. To get the turtle to tell what it needs to store, it does:
itemDetails = turtle.getItemDetail()
name = string.gsub(itemDetails.name, "minecraft:", "")
This gets the turtle to get the details of the item. It then sets a variable to be the name of the item minus the minecraft: at the beginning of it (I can't have a variable called "minecraft:cobblestone"). I don't know how to use this variable to call the table and find its position for the turtle to go to it. If anybody can help, I would appreciate any of the help. Thanks in advance!
Also, just a heads up, the code is still set up for debugging and testing purposes. The setup is a turtle, with an input chest in front of it, a fuel chest to the right, and the warehouse behind it.
I've tried doing:
cobblestone = {2,0,1}
--Putting a piece of cobblestone in--
itemDetails = turtle.getItemDetail()
--Returning name of "minecraft:cobblestone"--
name = string.gsub(itemDetails.name, "minecraft:", "")
print name[1]
So far this hasn't worked.
pos = {0,0,0}
looking = 0
cobblestone = {2,0,1}
function fuel()
if turtle.getFuelLevel() < 20 then
turtle.select(16)
turtle.refuel(1)
end
end
function left()
turtle.turnLeft()
looking = looking - 1
if looking < 0 then
looking = 3
end
print(looking)
end
function right()
turtle.turnRight()
looking = looking + 1
if looking > 3 then
looking = 0
end
print(looking)
end
function forward()
fuel()
if turtle.forward() then
if looking == 0 then
pos[1] = pos[1] - 1
elseif looking == 1 then
pos[3] = pos[3] - 1
elseif looking == 2 then
pos[1] = pos[1] + 1
elseif looking == 3 then
pos[3] = pos[3] + 1
else
print("wot")
end
end
end
function up()
fuel()
turtle.up()
pos[2] = pos[2] + 1
end
function down()
fuel()
turtle.down()
pos[2] = pos[2] - 1
end
function goHome()
while pos[3] > 0 do
while looking > 1 do
left()
end
forward()
end
while pos[2] > 0 do
down()
end
while pos[1] > 0 do
while looking > 0 do
left()
end
forward()
end
end
while true do
turtle.select(1)
while not turtle.suck() do
sleep(1)
end
itemDetails = turtle.getItemDetail()
name = string.gsub(itemDetails.name, "minecraft:", "")
print(name)
while looking < 2 or looking > 2 do
left()
end
for i = pos[1],name[1]-1 do
forward()
end
while looking > 3 or looking < 3 do
right()
end
for i = pos[3],name[3]-1 do
forward()
end
for i = pos[2],name[2]-1 do
up()
end
while looking < 2 or looking > 2 do
left()
end
turtle.select(1)
turtle.drop()
goHome()
end
I want to be able to do:
cobblestone = {2,0,1}
--Putting a piece of cobblestone in--
itemDetails = turtle.getItemDetail()
--Returning name of "minecraft:cobblestone"--
name = string.gsub(itemDetails.name, "minecraft:", "")
print name[1]
and have the turtle print the same thing as
cobblestone[1]
which would be 2. However, it returns with nil.
Currently, your cobblestone is global, what is bad for this example. At first, store all elements like this:
local myelems = {
cobblestone = {2,0,1};
grass = {3,1,2};
}
and so on. Use local variables when you can, it's good practice in programming at all.
So let's look at your code:
-- Returning name of "minecraft:cobblestone"
local name = string.gsub(itemDetails.name, "minecraft:", "")
print(name[1])
What it does? (I've changed it a bit)
At first,string.gsub look like an overkill here, use string.match instead:
local name = itemDetails.name:match("^minecraft:(.+)$")
Here itemDetails.name:match is same as calling string.match with itemDetails.name as first argument. It can be used for all string functions.
So here name is string variable. Indexing strings in different languages may have different results. In lua in actually provide access to all string functions to make calls to them easier (as above). There is no such function as string[1], so you get nil. However, now we have table myelems in which keys are strings and values are your tables.
Full code:
-- Table for all elements
local myelems = {
cobblestone = {2,0,1};
grass = {3,1,2};
}
-- Putting a piece of cobblestone in
local itemDetails = turtle.getItemDetail()
--Returning name of "minecraft:cobblestone"
local name = itemDetails.name:match("^minecraft:(.+)$")
-- Get our table
local elem = myelems[name]
if elem then
print(elem[1], elem[2], elem[3])
end

Why is initializing variables so important?

Please can someone explain to me, why NOT initializing first_idx and last_idx causes the code not to run??
When I run it I get this error "undefined local variable or method last_idx". I know that the advice is to always initialize the variables, but I don't understand why. After all first_idx and last_idx will ALWAYS get a value inside the loop because the argument letter is always present in the string (in this particular problem).
I'd really appreciate some (simple) insight. Thank you!
P.S, I also know that the problem is easily solved using #index and #rindex in Ruby, but I'm not allowed to solve it using straightforward methods.
def find_for_letter(string, letter)
first_idx = nil
0.upto(string.length - 1) do |idx1|
if string[idx1] == letter
first_idx = idx1
break
end
end
last_idx = nil
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
last_idx = idx2
break
end
end
if last_idx == first_idx
return [first_idx]
else
return [first_idx, last_idx]
end
end
def first_last_indices(word)
h = {}
word.chars.each do |char|
h[char] = find_for_letter(word, char)
end
h
end
Variables in block
From the Ruby Programming Language:
Blocks define a new variable scope: variables created within a block
exist only within that block and are undefined outside of the block.
Be cautious, however; the local variables in a method are available to
any blocks within that method. So if a block assigns a value to a
variable that is already defined outside of the block, this does not
create a new block-local variable but instead assigns a new value to
the already-existing variable.
a = 0
2.times do
a = 1
end
puts a #=> 1
b = 0
2.times do |i;b| # <- b will stay a block-local variable
b = 1
end
puts b #=> 0
2.times do |i|
c = 1
end
puts c #=> undefined local variable or method `c' for main:Object (NameError)
Refactoring your code
Iterating with chars and index
Here's a smaller method for your goal.
It keeps a hash with minmax indices for each character.
The default hash value is an empty array.
The method iterates over each character (with index).
If minmax array already contains 2 values :
it replaces the second one (max) with current index.
it adds current index to the array otherwise.
def first_last_indices(word)
minmax_hash = Hash.new { |h, k| h[k] = [] }
word.each_char.with_index do |char, index|
minmax = minmax_hash[char]
if minmax.size == 2
minmax[1] = index
else
minmax << index
end
end
minmax_hash
end
p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
With group_by
Here's another possibility. It uses group_by to get all the indices for each character, and minmax to get just the first and last indices :
def first_last_indices(word)
word.each_char.with_index
.group_by{ |c, _| c }.map{ |c, vs|
[c, vs.map(&:last).minmax.uniq]
}.to_h
end
p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
Even if you do not declare last_idx, you can still initialise it inside the loop, i.e.:
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
last_idx = idx2 # works absolutely fine
break
end
end
However notice where you declared the variable. Its a local variable and hence its tied to the block you are in. Now when you try to access that variable outside the block, you get the error:
undefined local variable or method last_idx
To make the variable available outside the block, you have to declare it outside. That is what you are doing when you declare last_idx = nil before the block where its assigned a value.
UPDATE:
Though by using instance variables you can avoid declaration, the best practices suggests it should be used in cases where information that these variables have is relevant to all or almost all of the class. On the other hand, if the information is very much limited to this particular method use local variables.
This is just the way that local variables work.
If you use instance variables, Ruby will assume that they have been initialised inside the conditional block, but will not for local variables.
def find_for_letter(string, letter)
0.upto(string.length - 1) do |idx1|
if string[idx1] == letter
#first_idx = idx1
break
end
end
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
#last_idx = idx2
break
end
end
if #last_idx == #first_idx
return [#first_idx]
else
return [#first_idx, #last_idx]
end
end
This works fine.

Converting base-10 to base2 in Ruby using recursion (Binary converter)

I would like to convert a base10 number to base2 in Ruby without using the built in to_s(2) method, using recursion.
I wrote this:
def to_binary(d)
if d<1
return ""
else
return to_binary(d/2).to_s + (d%2).to_s
end
end
This would return all correct results EXCEPT 0. Is there any way to return 0 for 0 without having leading zeroes for numbers greater than 0?
You can modify your checks a bit:
def to_binary(d)
return d.to_s if [0,1].include?(d) # same as "if d == 0 || d == 1"
to_binary(d/2) + (d%2).to_s
end
to_binary(10) == "1010"
#=> true
to_binary(0) == "0"
#=> true
You could also write above method as:
def to_binary(d)
return d.to_s if [0,1].include?(d)
div,mod = d.divmod(2)
to_binary(div) + mod.to_s
end

How to sort a hash by a function depending on values from the hash

I want to sort a hash by the following conditions:
An object that has hash[:sold] as false comes before an object that has hash[:sold] as true.
If both objects have hash[:sold] as false enter code here, then compute two variables distance_a, distance_b for the two hashes. I want object a to come before object b if distance_a < distance_b.
Code:
curr_hash.sort do |a,b|
status_a = a[1][:sold]
status_b = b[1][:sold]
if status_a == false && status_b == false
a_first_loc = [a[1][:loc_id], current_loc_id].min
a_second_loc = [a[1][:loc_id], current_loc_id].max
b_first_loc = [b[1][:loc_id], current_loc_id].min
b_second_loc = [b[1][:loc_id], current_loc_id].max
distance_a = LocationDistance.find_by(city_id: current_city.id, loc_a: a_first_loc, loc_b: a_second_loc).distance
distance_b = LocationDistance.find_by(city_id: current_city.id, loc_a: b_first_loc, loc_b: b_second_loc).distance
distance_a <=> distance_b
else return status_a == false ? 1 : 0
end
end
Borrowing #Maxim's distance function and using sort_by means the distance is only calculated once for each item.
Extracting distance as a function also has the advantage that it can be tested independently
def distance(hash)
LocationDistance.find_by(loc_a: hash[1][:loc_id], current_loc_id].min,
loc_b: [hash[1][:loc_id], current_loc_id].max,
city_id: current_city.id).distance
end
a.sort_by(|x| [true ? 1 : 0, distance(x)])
This has to do the job nicely for you:
def distance(hash)
LocationDistance.find_by(loc_a: hash[1][:loc_id], current_loc_id].min,
loc_b: [hash[1][:loc_id], current_loc_id].max,
city_id: current_city.id).distance
end
a.sort{|a,b| a[:sold] == b[:sold] ? ((distance(a) < distance(b)) ? -1 : 1) : (a[:sold] ? 1 : -1)}

Attempt to index field 'other'

What is the problem here? This code is supposed to remove texts after I shoot it and at the same time increasing the score. Also, can someone explain what does the other.name actually mean? I don't quite fully understand it..(And yes its the first if statement that has the error)
function wordCollision(e)
if (e.other.name == 'balloonText') then -- error here: attempt to index field 'other'(a nil value)
display.remove(e.other)
e.other = nil
score.text = score.text + 50
score.anchorX = 0
score.anchorY = 0
score.x = 200
score.y = 50
target.text = target.text - 1
else
if (e.other.name == 'balloonTextt') then
display.remove(e.other)
e.other = nil
score.text = score.text + 50
score.anchorX = 0
score.anchorY = 0
score.x = 200
score.y = 50
target.text = target.text - 1
end
end
end
It simply means that there is no entry with key 'other' in the table e.
If if you want to look up something in e.other you'll have to assign a table to that key:
e.other = {}
Using metatables, you could make it go automatically:
mt = {}
mt.__index=function(t,k) if ~rawget(t,k) then t[k]=setmetatable({},mt) end return t[k] end
e={}
e=setmetatable(e,mt)
e.other.name='foo'
Watch out with this though, because any lookup to a non-existant field will create a new table for it, which may or may not be what you want (aside of the fact that this overwrites any existing metatable on e):
for k,v in pairs(e) do print(k,v) end
print(e.bar)
for k,v in pairs(e) do print(k,v) end
Problem could be that you have the e.other = nil in there, but don't reset e.other to something after, so when the wordCollision() gets called again, it is e.other is nil. Could also be that e.other is never initialized in the first place. Verify it is initialized somewhere before wordCollision() is ever called, and verify that it is re-set to something between two calls to wordCollision().

Resources