I am building an application in rails, and I have an items_controller which contains the methods application for create, show, edit, destroy etc.
However, I am trying to create my own method to access all the values in a specific column of my database and I am having greatly difficulty in capturing this data in an array.
I have tried the following ways of capturing the data (where 'quantity' is the column in the database for which I looking for):
#items = Item.find(params[:id])
#items2 = #item.find(params[:quantity])
I have also tried:
#items = Item.find(params[:quantity])
& even:
#items = Item.all
#items2 = #item.find(params[:quantity])
However, none of these methods appear to be working. For what I am doing it is not even essential to
know which quantity column values relate to which row...just getting a list of the column values would suffice.
If any one knows what is wrong here, the help you be very greatly appreciated!
Thanks.
UPDATE:
For clarity I am trying to retrieve all the data for a particular column in my database associated with the items_controller and filter the data for a particular piece of data (in this case the string "7" - as the data is returned from the db as a string when using the Items.all method.
I then want a counter to increase each time the "7" is encountered in the quantity column.
def chartItems
#items = Item.find(params[:id])
#items2 = #items.find(params[:quantity])
#filter = custom_filter_for(#items2)
def custom_filter_for(value)
j=0 # counter initialised at 0
value.each do |x|
if x == "7" # checking for data equal to "7" - the num is retrieved as a string
j = j+1 # increase j counter by 1 whenever "7" is encountered as a quantity
end
return j
end
end
Your find parameter is handled as an id in this case:
#items = Item.find(params[:quantity])
All items are returned which has the id of your quantity parameter. This is clearly not what you want.
You can select Items based on quantity:
#items = Item.find_by_quantity(params[:quantity])
But if you need only the quantities in an array, this is what you are looking for:
#quantities = Items.select(:quantity).map(&:quantity)
Your updated question:
result = Items.find_by_quantity(params[:quantity]).count
In new versions of ActiveRecord, they've added the pluck which does essentially what #Matzi's select and map method does.
To get all item quantities, you could do
#quantities = Item.pluck(:quantity)
Also, I would double check your use of the find_by helpers. I think that find_by_quantity will only give you a single match back (#item, not #items). To get all, I think you really want to use where
#quantities = Item.where(:quantity => params[:quantity])
If you were to use the pluck I mentioned above, I think your filtering step could also be written pretty concisely. That filter is simply counting the number of 7's in the list, right?
#quantities = Item.pluck(:quantity)
#filtered_values = #quantities.select{|q| q == 7}.length
I hope this helps out.
Related
i think i used the right terminology for what i need, i currently have a database call in my home_controller that is returning a call to my database with all the entries in that table specified, Freelancer.
There is an attribute on these records that has either a true or false value, which is "featured".
I need a way to call a sort method, or some other way, on that object with the true being first and then the false being afterwards, i tried using this code
def index
#freelancers = Freelancer.all
p 'below im outputting featured freelancer i hope'
#freelancers.sort_by { |row| [row.featured ? 0 : 1, row.id]}
p #freelancers
end
But unfortunately this did not work, can anyone advise me on a way to get this to work? Id rather have the sorted object returned as is, rather then assigning it to a new one. Just for future features of adding pagy and a filter by cost.
Use order method
def index
#freelancers = Freelancer.order(featured: :desc)
end
I have an object that has an integer type of attribute. I want to subtract 1 from the attribute after a particular action. I tried in the controller:
def subtraction
#find a item and get the value, let's say value is 40
item = Item.where(id: params[:id]).pluck(:value)
# subtract 1 from the value and i thought it would be 40-1
after_subtraction = item.to_i - 1
#update the value
final = item.update(value: after_subtraction)
end
I get:
NoMethodError (undefined method `to_i' for [40]:Array
When I remove to_i, it says - is not a method. Is there any way to update the stored value?
The better way to handle is
item = Item.find_by(id: params[:id]).value
pluck will return you array, which is not necessary in your case here.
Based on the way you constructed the query, it gets the value for all entries that match the where condition, hence what is returned by the .pluck method is an array on which you cannot call .to_i method.
I guess what you want to do is to pluck the value you need from the first entry that matches your query, hence you can refactor as below
def subtraction
#find the first item with id supplied
item = Item.where(id: params[:id]).first
#after subtraction value
val = item.value - 1
#update the value
final = item.update(value: val)
end
As pluck returns an array you can not use to_i for conversion here.
Seeing your code, you can refactor it like this,
def subtraction
# Find the item first
item = Item.find(params[:id])
# Subtract 1 from the value column of the item
item.value -= 1
# Save the modification of the item
item.save!
end
You can't directly convert array in to to_i . please use below method
after_subtraction = item.join.to_i - 1
Yes, Nithin's answer is more valid. you can go with nithin's answer. you don't need to use pluck until you want array of multiple values.
I am making an application, part of whose code requires many if .. else conditions:
if #model_name == "Style"
if row.include? ('colors')
colors = row['colors'].split(';')
model.style_colors.concat Color.where('code IN (?)', colors).map {|i| i.id.to_s }
row.delete('colors')
end
if row.include? ('gender') and row.include? ('garments')
#garments = row['garments']
#gender = row['gender']
row.delete('garments')
row.delete('gender')
end
if row.include? ('sports')
#sports = row['sports']
row.delete('sports')
end
if row.include?('decoration_packages')
#decorations_packages = row['decoration_packages']
row.delete('decoration_packages')
end
model.attributes = row.to_hash.merge!(active: FALSE)
else
model.attributes = row.to_hash
end
I need to make objects of row hash to access subclasses, and then delete them from row so it can be saved to a model.
Any idea how I can minimize the use of conditions or optimize it?
There's a few optimisations here...
row.include? ('gender') and row.include? ('garments')
could be implemented as
['gender', 'garments'].all?{|x| row.include?(x)}
#garments = row['garments']
row.delete('garments')
could be implemented as
#garments = row.delete('garments')
You could actually squash a lot of these onto one line:
if row.include? ('sports')
#sports = row['sports']
row.delete('sports')
end
could be
#sports = row.delete('sports') if row.include? ('sports')
Also worth considering:
Do you need to delete the values from 'row'? Could you just retrieve the value?
What are you trying to do here? It looks like you're pulling a hash into instance variables... Which is what ActiveRecord does, basically. Could you just create a model with these attributes and then call it in this style?
Style.new(row)
if #model_name == "Style"
if row.include?('colors')
model.style_colors.concat(
Color.where(code: row.delete('colors').split(';')).pluck(:id).map(&:to_s)
)
end
if row.include?('gender') and row.include?('garments')
#garments = row.delete('garments')
#gender = row.delete('gender')
end
if row.include?('sports')
#sports = row.delete('sports')
end
if row.include?('decoration_packages')
#decorations_packages = row.delete('decoration_packages')
end
model.attributes = row.to_hash.merge!(active: false)
else
model.attributes = row.to_hash
end
I would do something like this with your current code:
if #model_name == "Style"
row_key_set = row.keys.to_set
if row.include? 'colors'
colors = row['colors'].split(';')
color_ids = Color.where(code: colors).pluck(:id)
model.style_colors.concat(color_ids.map(&:to_s))
end
if row_key_set >= Set['gender', 'garments']
#garments = row.delete('garments')
#gender = row.delete('gender')
end
#sports = row.delete('sports')
#decorations_packages = row.delete('decoration_packages')
model.attributes = row.to_hash.merge(active: false)
else
model.attributes = row.to_hash
end
Instead of using Color.where('code IN (?)', colors) you can just use Color.where(code: colors).
Instead of using .map {|i| i.id.to_s } you can use pluck (.pluck(:id)) to get an array of color ids. This also makes for a quicker query since only the ids get fetched from the database instead of the whole records.
I personally like to use sets to check if multiple values are present in another set. For this reason I create the row_key_set variable row.keys.to_set. Now you can easily check certain keys are present on your hash by just checking if the key set is greater or equal than another set (thus being a superset). row_key_set >= Set['gender', 'garments'] With just one check you could leave this out, but if you have multiple checks this might be worth the trouble. I also find code written this way also more readable, but that's just personal peference.
You don't need to check if a key is present on a Hash, the documentation tells us the following:
Deletes the key-value pair and returns the value from hsh whose key is equal to key. If the key is not found, it returns nil.
This means you can leave out the include? check and write the result from the delete directly to the instance variable. If the key is not present nil will be set for the instance variable.
Lastly I would leave out the explanation mark in row.to_hash.merge!(active: false). The version without explanation mark doesn't alter the original array and reduces the chance on accidental side effects. You're saving the variable to model.attributes anyway and toss away the generated array from the to_hash method. It's normally better to use non-altering versions of methods, unless you explicitly want a certain effect to happen.
I'm working with the LinkedIn API to get companies' details. They are sending an XML response, so I simply converted the XML to a hash using the .to_hash method
This is a sample hash I'm getting: http://pastebin.com/1bXtHZ2F
in some companies they have more than one locations and contact information, i want to parse this data and get the details like phone number, city, postal_code etc.
The structure of the response is not consistent. Sometimes location field itself is missing or the postal_code is available only at the fourth location.
I tried two ways:
1.
def phone(locations)
(locations && locations["values"][0]["contactInfo"]["phone1"]) || nil
end
This is not working if the phone number is not available in the first array
2.
def phone(locations)
if locations["locations"]["total"].to_i == 1
locations["locations"]["location"]["contact_info"]["phone1"]
else
locations["locations"]["location"].each do |p|
if (!p["contact_info"]["phone1"].nil? || !p['contact_info'].nil?)
return p["contact_info"]["phone1"]
break
end
end
end
end
This is not working if the "location" hash itself is missing from the response. I need a solution where I can search with the keys "city", "phone" and "postal_code" and update if it is present. If it returns an array, parse the array and get the non-empty data.
I've also read this StackOverflow answer.
I see this as a question about code confidence. That is, I'm betting you can figure out how to guess your way through all the possible conditions... but that will create a mess of unconfident code. Confident code states what it wants and it gets it and moves on. (Note: I get all of my inspiration on this topic from this wonderful book: http://www.confidentruby.com/ by Avdi Grimm).
That said, I'd recommend the following.
Install the naught gem: https://github.com/avdi/naught
In your code, utilize the Maybe conversion function (read through the gem documetnation for info) to confidently arrive at your values:
At the top of your class or controller:
NullObject = Naught.build
include NullObject::Conversions
In your method:
def phone(locations)
return {} if locations["location"].blank?
Maybe(locations["locations"])["location"].to_a.inject({}) do |location, acc|
contact_info = Maybe(location["contact_info"])
acc[location][:city] = contact_info["city1"].to_s
acc[location][:phone] = contact_info["phone1"].to_i
acc[location][:postal_code] = contact_info["postal_code1"].to_s
acc
end
end
I'm not sure exactly what you're trying to accomplish but the above may be a start. It is simply attempting to assume all of the keys exist. Whether they do or they don't they get converted to a object (an array, a string or an integer). And then, ultimately, collected into a hash (call acc -- short for "accumulator" -- internal to the loop above) to be returned.
If any of the above needs clarification let me know and we can chat.
Ok, this code basically works through the hash and isn't concerned about node names (other than the specific nodes it's searching for)
the find_and_get_values method takes two arguments: object to search, and an array of nodes to find. It will only return a result if all nodes in the array are siblings under the same parent node. (so "city" and "postal_code" must be under the same parent otherwise neither is returned)
The data returned is a simple hash.
The get_values method takes one argument (the company hash) and calls find_and_get_values twice, once for %w(city postal_code) and once for %w(phone1) and merges the hash results into one hash.
def get_values(company)
answer = {}
answer.merge!(find_and_get_values(company["locations"], %w(city postal_code))
answer.merge!(find_and_get_values(company["locations"], ["phone1"]))
answer
end
def find_and_get_values(source, match_keys)
return {} if source.nil?
if source.kind_of?(Array)
source.each do |sub_source|
result = find_and_get_values(sub_source, match_keys)
return result unless result.empty?
end
else
result = {}
if source.kind_of?(Hash)
match_keys.each do |key|
result[key] = source[key] unless source[key].nil?
end
return result if result.count == match_keys.count
source.each do |sub_source|
result = find_and_get_values(sub_source, match_keys)
return result unless result.empty?
end
end
end
return {}
end
p get_values(company)
I have some legacy code in my app that compares all table rows for a model with the result of an API call from a supplier.
This worked fine until last week where both the number of table rows and the number of results by the supplier increased massively.
The table cardinality has went from 1,657 to 59,699 and the number of results returned by the API is ~ 150,000.
What the code is doing is looking in the API results to check that if the current table row data is not found, if so then the current data is orphaned in the database since it exists there but not in what the supplier has given us.
Looking through 150,000 results to check if something isn't there doesn't sound particularly clever to me and that looks to be the case as I don't even know how long this takes to run as the view is still loading after about half an hour :/
Controller
#telco_numbers = TelcoNumber.orphaned_in_db
Model
def self.orphaned_in_db
db_numbers = self.find(:all)
listed_numbers = self.all_telco
orphaned_numbers = []
db_numbers.each do |db|
scan = listed_numbers.select{ |l| l.number == db.number}
orphaned_numbers.push(db) if scan.empty?
end
return orphaned_numbers
end
def self.some_telco(per_page, page = 1)
page = 1 if page.nil?
# this is the first api call which returns a link which is then used for the next api call
api_call = TelcoApiv3.new("post", "/numbers/#{TelcoApiv3.account_id}/allocated/all")
listed_numbers = TelcoApiv3.poll(api_call.response["link"])
return listed_numbers.collect do |ln|
ln.store("countrycode", ln["country_code"])
TelcoNumber.new ln
end
end
def self.all_telco(page = 1)
listed_numbers = some_telco(##max_nlist_results, page)
if listed_numbers.length == ##max_nlist_results
return listed_numbers.concat(all_telco(page + 1))
else
return listed_numbers
end
end
Example API result format:
[{"country_code":"44","number":"1133508889"},....
The number relates to the number column in the table for the model. (It is stored as a varchar and not as a number).
Also, the api results are returned in ascending number order so are already sorted so I would have thought that would have made things better than they are?
Why are you not trying Array difference. First make two arrays of db_numbers & listed_numbers and subtract the smaller array from the bigger one like this:
def self.orphaned_in_db
db_numbers = self.find(:all).map{|x| x.number}
listed_numbers = self.all_telco.map{|x| x.number}
orphaned_numbers = db_numbers - listed_numbers
orphaned_results = self.find(orphaned_numbers)
return orphaned_results
end
When I will subtract the listed_numbers from db_numbers, I will get the non-matching results set. and now you can find the results on the basis of orphaned_numbers in your database. It will be much faster. Thanks