push object in a class array with ruby [closed] - arrays

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
Hi i tried alot to push the variables which are in initialize to a class array but it always give error like this 'uninitialized class variable ##books in Object (NameError)'
code is here:
class Library
##books = Array.new
##books = []
attr_reader :bookName, :author
def initialize(bookName,author)
#bookName = bookName
#author = author
##books.push(self)
end
def countbooks
$count = 0
##books.each do |b|
count += 1
end
end
i used ##books.push(self) to take all variables of object to an array but it did not work.

DISCLAIMER: I haven't used Ruby for some time, so some of my knowledge might be outdated. Feel free to correct me.
There are multiple problems with your code. Let's get the code you provided into some sort of running state.
First of all, you are missing an end in your provided code within the countbooks method. It needs an end to close the loop. Second, you can use either Array.new or [] as they both create an empty array, but if you use both you just overwrite the first new, empty array.
If you now initialize an instance of your new class and call the method countbooks, you will receive an error because you don't access your global variable $count, but count instead, which has never been initialized. If you fix this, you can now just call the countbooks method and then a puts($count) and you get the current number.
However, global variables are generally bad practices, so you should avoid them. Remove the $ sign and add a return count after the loop ends, and we kind of replicated the Array.length method, just a lot slower :)
This should yield the following code:
class Library
##books = []
attr_reader :bookName, :author
def initialize(bookName,author)
#bookName = bookName
#author = author
##books.push(self)
end
def countbooks
count = 0
##books.each do |b|
count += 1
end
return count
end
end
The code should now be executable, but there is a big problem.
The class variable ##books:
You now have a single array for the whole class. That means, if we create a new instance of the class Library, the array will be shared across all of them.
x = Library.new("title","bintree")
y = Library.new("title2","bintree2")
puts(x.countbooks)
puts(y.bookName)
x.countsbooks will return 2 and you only push new entries to the array if you create a new instance of Library.
From your question, I understand that you now try to call ##books outside of the class. This will not work. It only exists in the Library class. You can not use the attr accessors to gain access, either.
You probably should read more on classes, instances, and especially the different variable types. These links helped me to answer your question:
More about classes, instances, and a bit about variables
More about arrays
Here is an example of how the code could look like if we ignore build-in functions such as Array.length to implement our own counter and avoid class (##) and global ($) variables. You can gain access to the #books if you now add attr_reader :books, but it would be better to write a method instead.
class LibraryBTD
def initialize
#books = Array.new
#counter = 0
end
def addBook(title,author)
#books.push([title,author])
#counter += 1
end
def listAllBooks
puts("There are currently " + #counter.to_s + " books in the library.")
#books.each do |book|
title = book[0]
author = book[1]
puts("Title: " + title + " | Author: " + author)
end
end
end
If we now create a new instance of LibraryBTD and add 2 books, we can list them with listAllBooks:
a = LibraryBTD.new
a.addBook("MyBook1","BTD")
a.addBook("MyBook2","BTD")
a.listAllBooks
This should output the following in the console:
There are currently 2 books in the library.
Title: MyBook1 | Author: BTD
Title: MyBook2 | Author: BTD
However, if we create a new instance, the list will be empty, because #books is only an instance variable.
b = LibraryBTD.new
b.listAllBooks
There are currently 0 books in the library.

Related

How to continuously send an array of serial data into multiple functions, with the data being updated everywhere? [duplicate]

This question already has answers here:
How do you run your own code alongside Tkinter's event loop?
(5 answers)
Closed 1 year ago.
I want to code a script, that has several different functions in which an array filled with serial data has to be received.
The serial data comes from an arduino every 1 seconds. (don't worry. I changed the code to a reproducible example by using a random array.)
What I've succeeded in so far is, that the code does send the array into the function example ONCE the first time and displays it as I want it to.
What it does not do yet, is, that the information inside the function gets updated as it comes in from the arduino. When you see the code, you're gonna say, well the data is only sent once. BUT when I randomize the array every second inside a loop, the loop obviously blocks the rest of the code and the gui won't build. The fact is, that serial read updates the array WITHOUT a loop, which is highly appreciated.
The question is: How do I transport this updating into the function. Remember: It would be the natural solution for the code below to simply insert the serial read stuff INSIDE the function. BUT this is just the code that boils down the issue. The real code has several widgets invoked inside several functions and I ended up copy-&-pasting THE ENTIRE serial data signal conditioning code block into EVERY function that needs the data. This significantly increased the lag of the code, and thus is no solution.
The example script contains commented out sections to make it easier to follow what I've been trying to do to solve it so far:
import numpy as np
#import rhinoscriptsyntax as rs
import time
import tkinter as tk
import serial
"""
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate = 500000,
#parity=serial.PARITY_NONE,
#stopbits=serial.STOPBITS_ONE,
#bytesize=serial.EIGHTBITS,
timeout=1
)
ser.flushInput()
ser.flushOutput()
#I've been trying to embed the serial read stuff inside a function itself, which I'd LOVE to implement,
#but it has the same problem: either it is called just once as it is written here, ore a loop blocks the code
def serial_data():
#serialData = ser.readline()
#serialData = serialData.decode()
#floats = [float(value) for value in serialData.split(',')]
#arr = np.array(floats)
arr = np.random.rand(100)
time.sleep(1)
return arr
"""
#above commented out and replaced with random array below.
#serialData = ser.readline()
#serialData = serialData.decode()
#floats = [float(value) for value in serialData.split(',')]
#arr = np.array(floats)
arr = np.random.rand(100)
time.sleep(1)
print(np.round(arr, 3))
def nextWindow(root):
frame1 = tk.Frame(root, width=800, height=500)
frame1.pack()
text= tk.Text(frame1, width=80, height= 12, bd=0)
text.pack()
text.delete("1.0",tk.END)
#serialData = serial_data()
#text.insert(tk.INSERT, serialData)
text.insert(tk.INSERT, np.round(arr, 3))
text.update()
root = tk.Tk()
root.title('PythonGuides')
root.geometry('300x200')
root.config(bg='#4a7a8c')
nextWindow(root)
root.mainloop()
A minimal example as to how to use .after "loops" (explanation in code comments):
import tkinter as tk
# for the example
counter = 1
# your serial_data function, renamed to
# be more self-explanatory
def get_serial_data():
# put the serial reading stuff here
# the counter is just an example
global counter
counter += 1
return str(counter)
def update_text(txt):
# get data, clear text widget, insert new data
serial_data = get_serial_data()
txt.delete('0.0', 'end')
txt.insert('end', serial_data)
# schedule this function to run again in 100ms
# so it will repeat all of this effectively
# updating the text widget, you don't need to call `.update`
# note that this is not recursive
root.after(100, update_text)
def create_text():
text = tk.Text(root)
text.pack()
update_text(text)
root = tk.Tk()
create_text()
root.mainloop()

How to parse data and store it into variables using Nokogiri and Ruby

When I assign variable names such as service_names and name_array they are nil and nothing goes to the class variable ##product_names.
I used Pry to try the code without storing it into a variable and it works. It has the values I need.
I had this split up in more variables before to make cleaner code, for example:
require 'pry'
require 'rubygems'
require 'open-uri'
require 'nokogiri'
class KefotoScraper::CLI
##product_names =[]
PAGE_URL = "https://kefotos.mx/"
def call
binding.pry
puts "These are the services that Kefoto offers:"
#list_products
puts "which service would you like to select?"
#selection = gets.chomp
view_price_range
puts "Would you like to go back to the service menu? y/n"
answer = gets.chomp
if answer == "y"
call
end
end
private
def home_html
# #home_html ||=
# HTTParty.get root_path
Nokogiri::HTML(open(PAGE_URL))
end
#
# # TODO: read about ruby memoization
# def home_node
#
# #home_node ||=
# Nokogiri::HTML(PAGE_URL)
# end
def service_names
#service_names = home_html.css(".nav-link").map do
|link| link['href'].to_s.gsub(/.php/, "")
end
#service_names.each do |pr|
##product_names << pr
end
end
def list_products
i = 1
n = 0
while ##product_names.length < n
##product_names.each do |list_item|
puts "#{i} #{list_item[n]}"
i += 1
n += 1
end
end
end
def view_price_range
price_range = []
#service_links.each do |link|
if #service = link
link.css(".row").map {|price| price["p"].value}
price_range << p
end
price_range
end
def service_links
#service_links ||=
home_html.css(".nav-item").map { |link| link['href'] }
end
end
end
##product_names should contain the code that comes out of
home_html.css(".nav-link").map { |link| link['href'] }.to_s.gsub(/.php/, "")
which later I turn back to an array.
This is what it looks like in Pry:
9] pry(#<KefotoScraper::CLI>)> home_html.css(".nav-link").map { |link| link['href'] }.to_s.gsub(/.php/, "").split(",")
=> ["[\"foto-enmarcada\"", " \"impresion-fotografica\"", " \"photobooks\"", " \"impresion-directa-canvas\"", " \"impresion-acrilico\"", " \"fotoregalos\"]"]
[10] pry(#<KefotoScraper::CLI>)> home_html.css(".nav-link").map { |link| link['href'] }.to_s.gsub(/.php/, "").split(",")[0]
=> "[\"foto-enmarcada\""
Nokogiri's command-line IRB is your friend. Use nokogiri "https://kefotos.mx/" at the shell to start it up:
irb(main):006:0> #doc.css('.nav-link[href]').map { |l| l['href'].sub(/\.php$/, '') }
=> ["foto-enmarcada", "impresion-fotografica", "photobooks", "impresion-directa-canvas", "impresion-acrilico", "fotoregalos"]
That tells us it's not dynamic HTML and shows how I'd retrieve those values. Since an a tag doesn't have to contain href parameters I guarded against retrieving any such tags by accident.
You've got bugs, potential bugs and bad practices. Here are some untested but likely to work ways to fix them:
Running the code results in:
uninitialized constant KefotoScraper (NameError)
In your code you have #service and #service_links which are never initialized so...?
Don't do this because it's cruel:
def home_html
Nokogiri::HTML(open(PAGE_URL))
end
Every time you call home_html you (re)open and (re)read the page from the remote site and wasting your and their CPU and network time. Instead, cache the parsed document in a variable kind of like you did in your commented-out line using HTTParty. It's much more friendly to not hit sites repeatedly and helps avoid getting banned.
Moving on:
def service_names
#service_names = home_html.css(".nav-link").map do
|link| link['href'].to_s.gsub(/.php/, "")
end
#service_names.each do |pr|
##product_names << pr
end
end
I'd use something like get_product_names and return the array like I did in Nokogiri above:
def get_product_names
get_html.css('.nav-link[href]').map { |l|
l['href'].sub(/\.php$/, '')
}
end
:
:
##product_names = get_product_names()
Here's why I'd do it another way. You used:
link['href'].to_s.gsub(/.php/, "")
to_s is redundant because link['href'] is already returning a string. Stringizing a string wastes brain cycles when rereading/debugging the code. Be kind to yourself and don't do that.
require 'nokogiri'
html = '<a href="foo">'
doc = Nokogiri::HTML(html)
doc.at('a')['href'] # => "foo"
doc.at('a')['href'].class # => String
gsub Ew. How many occurrences of the target string do you anticipate to find and replace? If only one, which is extremely likely in a URL "href", instead use sub because it's more efficient; It only runs once and moves on whereas gsub looks through the string at least one additional time to see if it needs to run again.
/.php/ doesn't mean what you think it does, and it's a very subtle bug in waiting. /.php/ means "some character followed by "php", but you most likely meant "a period followed by 'php'". This was something I used to see all the time because other programmers I worked with didn't bother to figure out what they were doing, and being the senior guy it was my job to pick their code apart and find bugs. Instead you should use /\.php/ which removes the special meaning of ., resulting in your desired pattern which is not going to trigger if it encounters "aphp" or something similar. See "Metacharacters and Escapes" and the following section on that page for more information.
On top of the above, the pattern needs to be anchored to avoid wasting more CPU. /\.php/ will cause the regular expression engine to start at the beginning of the string and walk through it until it reaches the end. As strings get longer that process gets slower, and in production code that is processing GB of data it can slow down a system markedly. Instead, using an anchor like /\.php$/ or /\.php\z/ gives the engine a hint where it should start looking and can result in big speedups. I've got some answers on SO that go into this, and the included benchmarks show how they help. See "Anchors" for more information.
That should help you but I didn't try modifying your code to see if it did. When asking questions about bugs in your code we need the minimum code necessary to reproduce the problem. That lets us help you more quickly and efficiently. Please see "ask" and the linked pages and "mcve".

How to pass an array between Ruby files

I want to pass an array from a Ruby file to another one.
I have a three files:
main.rb
company.rb
applicant.rb
Here is the code for main.rb:
require './src/company.rb'
require './src/applicant.rb'
company = Company.new('data/boundless.json')
company.find_applicants('google')
Here is the code for company.rb:
require 'json'
require_relative 'applicant.rb'
class Company
attr_accessor :jobs , :arrOfApp
def self.load_json(filepath)
file = File.read(filepath)
return JSON.parse(file)
end
def initialize(filePath)
# Load the json file and loop over the jobs to create an array of instance of `Job`
# Assign the `jobs` instance variable.
jobs=Array.new
data_hash = Company.load_json(filePath)
numberOfJobs= data_hash['jobs'].length
for i in 0 ... numberOfJobs
jobs[i]=data_hash['jobs'][i]['applicants']
# puts jobs
end
end
## TODO: Impelement this method to return applicants from all jobs with a
## tag matching this keyword
def find_applicants(keyWord)
app =Applicant.new
arrOfApp=Array.new
app.data_of_applicant(jobs)
end
end
And finally the code for applicant.rb:
require_relative 'company.rb'
class Applicant
attr_accessor :id, :name, :tags
def initialize
end
def data_of_applicant(j)
id=Array.new
name=Array.new
tags=Array.new
puts j
end
end
The program reads a JSON file to get some information from it. Whenever I try to print the value being sent to the applicant file nothing is printed.
You can't pass an array from a ruby file to another one., you only can pass data between classes and objects.
Other possibilities which may help:
constants (Defined with starting capital letter)
global variables (starting with $)
Singletons
To keep data inside the class instances (objects) you need attributes (variables starting with #).
You can find this concepts in every beginner manual of ruby (and if not, then the manual is not worth to be used)
You made another common error.
Let's check it with a small example:
class Company
attr_accessor :jobs
def initialize()
jobs='This should be assigned to my accessor jobs'
end
end
puts Company.new.jobs
The result is an empty line.
What happend? In the initialize-method you define a local variable jobs. Local means, it is only available in the method ans is lost when the method leaves.
Correct would be 1) using the instance variable:
class Company
attr_accessor :jobs
def initialize()
#jobs='This should be assigned to my accessor jobs'
end
end
or 2) using the accessor method:
class Company
attr_accessor :jobs
def initialize()
self.jobs='This should be assigned to my accessor jobs'
end
end
In both cases the puts Company.new.jobs returns the text you defined.
See also Ruby instance variable access
if i'm reading this correctly, you're asking ruby to make the calculation, but never stating that it should be printed. i believe changing the last line of your main.rb to this:
puts company.find_applicants('google')
should suffice.

Logical-Indexing for Matlab-object-arrays

Is there any way in Matlab R2011b to apply logical-indexing to object-arrays? The objects which fulfill specific condition(s) regarding their properties should be returned. At best the solution is also possible with object-arrays that are a property of another object (aggregation).
In my project there are a lot of entities which have to be identified by their manifold features. Matlab objects with their properties provide a clear data foundation for this purpose. The alternative of using structs (or cells) and arrays of indices seems to be too confusing. Unfortunately the access to the properties of objects is a little bit complicated.
For Example, all Objects in myArray with Element.val==3 should be returned:
elementsValIsThree = myElements(Element.val==3);
Best solution so far:
find([myElements.val]==3);
But this doesn't return the objects and not the absolute index if a subset of myElements is input.
Another attempt returns only the first Element and needs constant properties:
myElements(Element.val==3);
A minimal example with class definition etc. for clarification:
% element.m
classdef Element
properties
val
end
methods
function obj = Element(value)
if nargin > 0 % to allow empty construction
obj.val = value;
end
end
end
end
Create array of Element-Objects:
myElements(4) = Element(3)
Now myElements(4) has val=3.
I'm not sure I understood the question, but the logical index can be generated as
arrayfun(#(e) isequal(e.val,3), myElements);
So, to pick the elements of myElements whose val field equals 3:
elementsValIsThree = myElements(arrayfun(#(e) isequal(e.val,3), myElements));

Queries with JDOQL Unique Result Set

In my GAE application I want to make a JDOQL which returns a List where every element exist at most once even in the database there are more. How can I do this?
I dont know about JDOQL, but if you want a list where each entity exists utmost once i.e each list element is unique, then you could do the following:
Asume you have an entit type / model class that we call Type, with attributes att1,attr2.
You want to have a list of unique elements based on one or more of the attributes, say attr2.
you can use the following method that I adapted from a good source on the issue:
def unique(seq, idfun=None):
''' A function that returns a list of unique items in a very efficient manner
Refer to : http://www.peterbe.com/plog/uniqifiers-benchmark '''
# order preserving
if idfun is None:
def idfun(x): return x
seen = {}
result = []
for item in seq:
marker = idfun(item)
# in old Python versions:
# if seen.has_key(marker)
# but in new ones:
if marker in seen: continue
seen[marker] = 1
result.append(item)
return result
the to get a list of unique elements from the datastore type Type based on attr2 i could do something like:
list = Type.all()
unique_list = unique(list,lambda t: t.attr2)
Hope this helps because it has been the best method for me so far.

Resources