Iterate through array of column names to pass to uniq.pluck()? - ruby-on-rails

I have multiple columns I need to pull unique values from and compile an array of each unique value. Using uniq.pluck(:column_name) works, but how do I iterate over an array of column names?
react = []
fields = [reactivity_1, reactivity_2, reactivity_3, reactivity_4]
fields.each do |field|
puts "Parsing #{field}"
Raw.all.uniq.pluck(field).each do |r|
unless react.include? r
puts "Adding #{r} to Array."
react << r
else
puts "#{r} Exists."
end
end
end
Error:
NameError: undefined local variable or method `reactivity_1' for main:Object

You will need to make the column names strings or symbols, like this Ruby thinks it is a local varaible or method.
react = Set.new
fields = [:reactivity_1, :reactivity_2, :reactivity_3, :reactivity_4]
fields.each do |field|
puts "Parsing #{field}"
Raw.all.uniq.pluck(field).each do |r|
react << r
end
end
If you want to make sure that a collection does not contain duplicate, you can use a Set:
require "set"
set = Set.new
set << "foo"
set << "bar"
set << "bar"
puts set.size #> 2
I've rewritten your code sample to use Set.
Can you describe what you are trying to achieve? Perhaps there is an easier way to get the data out of the DB.

Related

How to create an instance of a class in Rails, giving each new instance 5 has_many through relationships

I want to make an instance of my Test class get five practice questions through a join class when it is initialised. If a test is an "exam" then it should just get 5 exam questions without a join class. (the question types have different models)
So far It doesn't behave the way I expect
self.practice_questions = []
it makes 5 join classes every time, but the array of self.practice_questions stays empty.
def get_questions
puts "ASDASDASDASDSAD"
array = []
if self.for_practice
puts "ASDASDASOASKODKSAOKDASODKOASKDSAOKDOASKDOASK"
PracticeQuestion.sort_for_selection[0...5].each do |question|
array << question
question.use_practice_question
end
elsif for_practice === false
puts self.exam_questions
if self.exam_questions.length ===0
grab 5 unused exam type questions
ExamQuestion.unused[0...5].each do |question|
puts "grabbing question #{question.title}"
question.test = self
question.use_question
end
end
puts "hello"
puts self.practice_questions.length
self.practice_questions ||= array
self.save
puts self.practice_questions.length
self.practice_questions.each {|question| puts question.title}
end
self.practice_questions ||= array will only assign the array if self.practice_questions is false or nil, are you sure it's one of those?
If practice_questions is a has_many, try one of this:
array.each do |el|
self.practice_questions << el
end
or:
self.practice_questions_ids = array.map(&:id)
https://guides.rubyonrails.org/association_basics.html#has-many-association-reference

How do I iterate through a table and create a hash for each value?

I have two tables. One for accounts and another for keywords. I would like to iterate over all of the keywords and store each one in a hash--grouped by the account ID that added the keyword. The code that I have below doesn't add each keyword to the hash. For example, I have an account that has 2 keyword entries. My code skips the first entry and only adds the second entry to the hash.
#keyword_hash = {}
#account.each do |key, value|
#keywords.where(:profile_id => key).each do |keyword|
#keyword_hash[key] = keyword.entry
end
end
puts #keyword_hash
How can I modify the above code so that I add each keyword entry for a particular account to the hash?
I would like to be able to do #keyword_hash[6] and get keyword1, keyword2, keyword3, etc. for that account. Thanks!
Make an array [keyword1, keyword2, keyword3, etc.] and then add it to hash
**
#keyword_hash = {}
#account.each do |key, value|
arr = []
#keywords.where(:profile_id => key).each do |keyword|
arr << keyword.entry
end
#keyword_hash[key] = arr
end
puts #keyword_hash
**
Try this code
#keyword_hash = Hash.new { |h, k| h[k] = [] }
#account.each do |key, value|
#keywords.where(:profile_id => key).each do |keyword|
#keyword_hash[key] << keyword.entry
end
end
puts #keyword_hash
The mistake you are doing is that you are storing a single value against each key in your #keyword_hash hash. so when your code writes second value against account key, it replaces the previous value instead of adding second one.
Edit: Thank you #mudasobwa for correction regarding shared default value.
#keyword_hash = Hash.new { |hash, key| hash[key] = [] }
#keywords.group_by{ |k| k.profile_id }.each do |key,value|
#keyword_hash[key] = value.map(&:entry)
end
puts #keyword_hash
After doing some more research, I found the above solution. I used #jvillian's suggestion about group_by, and I found this article that showed me how to initialize the hash.

RSpec testing model method

I have this method in my models/images.rb model. I am starting with testing and having a hard time coming up with tests for it. Would appreciate your help.
def self.tags
t = "db/data.csv"
#arr = []
csvdata = CSV.read(t)
csvdata.shift
csvdata.each do |row|
row.each_with_index do |l, i|
unless l.nil?
#arr << l
end
end
end
#arr
end
First off a word of advice - CSV is probably the worst imaginable data format and is best avoided unless absolutely unavoidable - like if the client insists that manipulating data in MS Excel is a good idea (it is not).
If you have to use CSV don't use a method name like .tags which can confused for a regular ActiveRecord relation.
Testing methods that read from the file system can be quite difficult.
To start with you might want to alter the signature of the method so that you can pass a file path.
def self.tags(file = "db/data.csv")
# ...
end
That way you can pass a fixture file so that you can test it deterministically.
RSpec.describe Image do
describe "tags" do
let(:file) { Rails.root.join('spec', 'support', 'fixtures', 'tags.csv') }
it 'returns an array' do
expect(Image.tags(file)).to eq [ { foo: 'bar' }, { foo: 'baz' } ]
end
end
end
However your method is very ideosyncratic -
def self.tags
t = "db/data.csv"
#arr = []
self.tags makes it a class method yet you are declaring #arr as an instance variable.
Additionally Ruby's enumerable module provides so many methods for manipulating arrays that using an outer variable in a loop is not needed.
def self.tags(file = "db/data.csv")
csv_data = CSV.read(file)
csv_data.shift
csv_data.compact # removes nil elements
end

using print inside def having yield statement

I am trying to print inside a function.
The function is used for invoking a block.
But I don't see the print happening in the function definition.
Please shed a light on this. Basically I am not clear with the control flow.
def find_all
matching_items = []
self.each do |item|
if yield(item)
puts "after yield" #print not happening
matching_items << item
end
end
matching_items
end
p ['a', 'b','c','c'].find_all { |item|
if item == 'a'
true
end
}
If your code is exactly as written, you are defining and independent method find_all defined on main. When you type [1,2,3,4].find_all, you are calling the find_all method on Array, which is defined in the Enumerable method. So you are not calling your method at all.
What you are probably trying to do is
class Array
def find_all
...
end
end
This way, [1,2,3,4].find_all will call this method.
However, note that this is probably a bad idea: you're overriding a core method that in a class that isn't yours, so that could have consequences in other code that you are not able to anticipate if any other code uses the find_all method.
What you might try instead is to define a method that takes the array in as an argument. You might move this to a module, but for now:
def find_all(array)
matching_items = []
array.each do |item|
if yield(item)
puts "after yield" #print not happening
matching_items << item
end
end
matching_items
end
Of course, this is basically what Enumerable#find_all already does, but less efficiently: so perhaps this is just an academic exercise, in which case, great!, but otherwise, why not just use the existing method?
If you are trying to re-open the class Array then, this is how you can do it
class Array
def find_all(matching_items = [])
self.each do |item|
if yield(item)
puts "after yield" #print not happening
matching_items << item
end
end
matching_items
end
end
p ['a', 'b', 'c', 'c'].find_all { |item|
if item == 'a'
true
end
}
Output
after yield
["a"]

Rails loop through records in model

I have 2 hstore columns (parameters and keys) defined in my PostgeSQL database. I want to get a list of keys and have defined a method for it in the model:
def self.keys_list
logs = self
list = Log.column_names - %w{id parameters extras}
logs.each do |log|
log.parameters.present? ? list << log.parameters.keys : list << []
log.extras.present? ? list << log.extras.keys : list << []
end
list = list.flatten.uniq
return list
end
But when I try using it, I get the following error:
NoMethodError: undefined method `each' for #<Class:0x00000004b630b0>
Can anyone suggest where the error is or how to do it some other way?
ActiveRecord::Base does not define an .each method. You need to add in a call to all, like so:
all.each do |log|
#...
end
This should make both Log.keys_list and Log.where(name: "Peeyush").keys_list work.

Resources