Add to class value in ruby on rails data in loop - ruby-on-rails

I have such code:
def accum_search
if params[:akbcap].present?
akbcap_array = [12,18,19,20,25,30,35,36,38,40,41,42,44,45,46,47,50,52,53,54,55,56,58,60,61,62,63,64,65,66,68,69,70,71,72,74,75,77,80,85,88,90,91,92,95,98,100,102,110,115,120,125,130,135,140,170,180,185,190,192,200,210,220,225]
min, max = params[:akbcap].split('-').map {|s| s.to_i }
logger.warn("!#!!!!!!!!!!!! AAA !!!!!!!!!!")
logger.warn(min)
logger.warn(max)
caprange = min...max
sa = akbcap_array.select {|n| caprange.include? n }
##cross = OtherProductsCrossList.find(:all, :conditions => {:cross_value => 1})
cap = "*"+params[:akbcap]+"*"
sa.each do |s|
logger.warn(s)
#accums = Accumulator.by_capacity(s).by_size(params[:akbsize]).by_brand(params[:akbbrand])
end
else
#accums = Accumulator.by_capacity(50).by_size(params[:akbsize]).by_brand(params[:akbbrand])
end
end
As you see i have such part:
sa.each do |s|
logger.warn(s)
#accums = Accumulator.by_capacity(s).by_size(params[:akbsize]).by_brand(params[:akbbrand])
end
but could i add on every iteration in #accums data from search? now it has last value( I could done it via arrays... but how to do via class-variable?

Yes, initiate it before the loop and use the << operator to append. End with flatten to make it a single dimension array.
#accums = []
# ...
sa.each do |s|
#accums << Accumulator.several_method_calls......
end
#accums.flatten!
or for compactness:
result = sa.map{|s| Accumulator.several_method_calls...... }.flatten

Related

Ruby Rails Excel Format Array Manipulation

I need some help/idea on how to dynamically position values in a 2D array.
Basically, I have a condition that must be met and that determines which column should the value be written.
This is what I have (a loop that pass conditions before writing the value):
#records.each do |client|
client_info = Report.get_client_info(client)
client_address = Report.get_client_address(client)
client_records = []
client_records << [client_info.id, client_info.full_name]
if #include_address == "1"
client_address.each_with_index do |address, i|
if client_records[i].present?
client_records[i][2] = address.full_address
client_records[i][3] = address.address_type
else
client_records << ["","", address.full_address, address.address_type]
end
end
end
if #include_contact == "1"
client_contact.each_with_index do |contact, i|
if client_records[i].present?
client_records[i][4] = contact.value
client_records[i][5] = contact.label
else
client_records << ["","","","", contact.value, contact.label]
end
end
end
end
Problem is: IF I want to make another condition, and the condition in between the two (conditions) is not true, the position of the column value set by: client_records[i][2] >> 2,3 etc... is hard coded, instead of the third condition occupying the place of the second condition's columns, it naturally leaves it blank and stays at the same place.
This is how it would look like:
Instead of:
How can I work around this?
For example, try that:
#records.each do |client|
client_info = Report.get_client_info(client)
client_address = Report.get_client_address(client)
client_records = []
line = [client_info.id, client_info.full_name]
[client_address.count, client_contact.count].max.times do |i|
if #include_address == "1" && client_address[i]
line << client_address[i].full_address << client_address[i].address_type
else
line << '' << ''
end
if ##include_contact == "1" && client_contact[i]
line << client_contact[i].value << client_contact[i].label
else
line << '' << ''
end
client_records << line.clone
line = ['', '']
end
end

Remove element from an array of objects and create array of hash

Rewriting the question with detailed example:
I have array of objects with three property, I would like to build array of harsh and have the resulting hash not have #name instance property. I have simulated the problem with an example.
example creation
class Employee
attr_accessor :name, :company, :duration
def initialize(name, company, duration)
#name = name
#company = company
#duration = duration
end
end
aSong1 = Employee.new("Fleck", "AMZ", 260)
aSong2 = Employee.new("Taylor", "EMC", 120)
aSong3 = Employee.new("Bob", "Adobe", 260)
aSong4 = Employee.new("Jack", "Google", 360)
final_array = [ ]
final_array.push(aSong1)
final_array.push(aSong2)
final_array.push(aSong3)
final_array.push(aSong4)
controller
puts final_array.length #4
final_array.each do | element |
puts element.is_a?(Object) #true
puts element.name #prints name
end
resulting array ( expected )
result = [{company: 'AMZ',duration: 260}, {company: 'EMC',duration: 120},{company: 'Adobe',duration: 260}, {company: 'Google',duration: 360} ]
Example: repl
You can delete the id keys using
a = [{"id":"21","company":"AMC","name":"Matt"},{"id":"22","company":"AMC","name":"Jon"},
{"id":"12","company":"XYZ","name":"Bob"}].each{|o| o.delete :id}
If you want to compare it with other hashes you can use the merge method assuming a2 is the second Hash
a.merge(a2)
This will return a new hash with any matching keys updated with new corresponding values from the second hash
final_array.collect {|x| { company: x.company, duration: x.duration }}
result:
[{:company=>"AMZ", :duration=>260}, {:company=>"EMC", :duration=>120}, {:company=>"Adobe", :duration=>260}, {:company=>"Google", :duration=>360}]
How about the following?
class Employee
def to_h
instance_variables.each_with_object({}) do |var, h|
k = var.to_s.tr('#','').to_sym
v = instance_variable_get(var)
h[k] = v
end
end
end
result = final_array.map{|e| e.to_h.delete_if{|k,v| k == :name}}

Ruby how to force output 0 when count has no rows

#counseling = Counseling.ransack(params[:q])
#counselings = #counseling.result.joins('RIGHT JOIN "subjects" ON "subjects"."id" = "counselings"."subject_id"')
#result = {}
#result[:data] = #counselings.group(row_condition).count
Blockquote
def self.create_case_sql_for_nested_tree2(foreign_key)
modelClass = foreign_key.sub(/_id3$/, '').camelize.constantize
#when_then_conditions = modelClass.roots.map do |o|
o.children.map do |c|
c.children.map do |g|
idlist = g.self_and_descendants.pluck(:id)
"WHEN subject_id IN(#{idlist.join(',')}) THEN #{g.id}"
end
end
end
"CASE #{#when_then_conditions.join(' ')} ELSE null END"
end
According to your comment, You want to join Counseling and Subject model and count the subject_id. I think you can do it like this,
Counseling.joins(:subject).count(:subject_id)
If you want to put any where condition you can do it like this,
Counseling.joins(:subject).where("some condition").count(:subject_id)

Ruby method return more than one variable

I need to return to my Rails view more than one variable from method... But how could i do this?
For example now i have
def my1
#price = 1
#price
end
but how could i also return other valuem somethin like:
def my1
#qnt = 2
#price = 1
#price, #qnt
end
?
Also my idea is to split them to string like
#price + "/-/" + #qnt
and then just split them via /-/ in view.... But this is a bad practice...
So how could i get from one method two or more variables?
Return an array:
def my1
qnt = 2
price = 1
[price, qnt]
end
then you can do this:
p, q = my1() # parentheses to emphasize a method call
# do something with p and q
Option 2
Or you can return a custom object, like this:
require 'ostruct'
def my1
qnt = 2
price = 1
OpenStruct.new(price: price, quantity: qnt)
end
res = my1() # parentheses to emphasize a method call
res.quantity # => 2
res.price # => 1
Use another object that will hold the variables and return it. You can then access the variables from that object;
Array
The easiest way is to return an Array:
def my1
#qnt = 2
#price = 1
[ #price, #qnt ]
end
price, quantity = my1
Hash
But you could also return a Hash:
def my1
#qnt = 2
#price = 1
{ :quantity => #qnt, :price = #price
end
return_value = my1
price = return_value[:price]
quantity = return_value[:quantity]
# or
price, quantity = [ return_value[:price], return_value[:quantity] ]
Custom Class
Or a custom Class:
class ValueHolder
attr_reader :quantity, :price
def initialize(quantity, price)
#quantity = quantity
#price = price
end
end
def my1
#qnt = 2
#price = 1
ValueHolder.new(#qnt, #price)
end
value_holder = my1
price = value_holder.price
quantity = value_holder.quantity
# or
price, quantity = [ value_holder.price, value_holder.quantity ]
OpenStruct
You could use OpenStruct as Sergio mentioned.

How can I make a delayed job for this custom method?

Here is my Lesson model:
#encoding: utf-8
class Lesson < ActiveRecord::Base
attr_accessible :content, :title, :parsed_content, :html_content, :user_id
serialize :parsed_content, Array
serialize :html_content, Array
serialize :pinyin_content, Array
serialize :defined_content, Array
serialize :literal_content, Array
validates :title, :presence => true
validates :content, :presence => true
belongs_to :user
before_update do |lesson|
lesson.makesandwich
end
before_save do |lesson|
lesson.delay.makesandwich
end
def makesandwich
require 'rmmseg'
#require 'to_lang'
require 'bing_translator'
require 'ruby-pinyin'
self.parsed_content = []
RMMSeg::Dictionary.load_dictionaries
content = self.content
paragraphs = content.split(/\r\n\r\n/) #convert to array of paragraphs
self.parsed_content = paragraphs
paragraphs.each_with_index do |text, ti|
text = text.gsub("。", "^^.")
text = text.gsub("?", "~~?")
text = text.gsub("!", "||!")
text = text.gsub(":", ":") #fix missing colons
text = text.split(/[.?!]/u) #convert to an array
text.each do |s|
s.gsub!("^^", "。")
s.gsub!("~~", "?")
s.gsub!("||", "!")
#s.gsub!("———————————",":")
end
text.each_with_index do |val, index|
algor = RMMSeg::Algorithm.new(text[index])
splittext = []
loop do
tok = algor.next_token
break if tok.nil?
tex = tok.text.force_encoding('UTF-8')
splittext << tex
text[index] = splittext
end
paragraphs[ti] = text
end
end
bing = BingTranslator.new(BING_API)
self.parsed_content = paragraphs
textarray = Marshal.load(Marshal.dump(paragraphs))
self.defined_content = Marshal.load(Marshal.dump(paragraphs))
self.literal_content = Marshal.load(Marshal.dump(paragraphs))
self.pinyin_content = Marshal.load(Marshal.dump(paragraphs))
textarray.each_with_index do |paragraph, pi|
paragraph.each_with_index do |sentence, si|
sentence.each_with_index do |word, wi|
if DictionaryEntry.find_by_simplified(word) != nil
self.defined_content[pi][si][wi] = DictionaryEntry.find_by_simplified(word).definition
#self.literal_content is down below
self.pinyin_content[pi][si][wi] = DictionaryEntry.find_by_simplified(word).pinyin
else
self.defined_content[pi][si][wi] = bing.translate(word, :from => 'zh-CHS', :to => 'en')
#self.defined_content[pi][si][wi] = word
#self.literal_content is down below
if PinYin.of_string(word, true).length > 1 #for punctuation
self.pinyin_content[pi][si][wi] = PinYin.of_string(word, true).join(" ").downcase
else
self.pinyin_content[pi][si][wi] = word
end
end
end
end
end
#Literal
literalarray = Marshal.load(Marshal.dump(paragraphs))
literalarray.each_with_index do |paragraph, pi|
paragraph.each_with_index do |sentence, si| #iterate array of sentence
literalarray[pi][si] = []
sentence.each_with_index do |word, wi| #iterate sentence's array of words
entrytobesliced = DictionaryEntry.find_by_simplified(word)
slicedentry = []
if entrytobesliced == nil
if word.length > 1 && word !~ /\w/ #/^\s*\w\d+\s*$/ #number regex #for cases where there is no DictionaryEntry
split = []
wordarray = word.split("").each_with_index() do |ws, wsi|
split << [DictionaryEntry.find_by_simplified(ws).definition]
end
literalarray[pi][si] << split
else
literalarray[pi][si] << [word] #in case none of the above work
end
else
entrytobesliced.simplified.each_char do |w|
singlechar = DictionaryEntry.find_by_simplified(w)
slicedentry << singlechar.definition.split("\", \"")
end
literalarray[pi][si] << slicedentry
end
self.literal_content = literalarray #slicedentry #literalarray
end
end
end
end
end
When I try to create a new lesson it errors like this: Jobs cannot be created for records before they've been persisted
But if I change it to after_save instead of before_save then I can see the work run, but it doesn't update the serialized arrays in the database.
Can someone please help me implement delayed_jobs for this? It was working when I had:
before_save do |lesson|
lesson.makesandwich #no delay
end
I think you're getting these errors:
Jobs cannot be created for records before they've been persisted
because your Lesson instances won't have an id until they've been saved and without an id, DJ has no way to know which instance it should be working with. So you have to use an after_save so that your Lesson has an id and can be uniquely identified. But then your updates from the delayed job won't be saved because, well, nothing asks for them to be saved. You should be able to get around that simply by adding a self.save or self.save! call at the end of makesandwich.

Resources