Select item in array that matches one item in another array - ruby-on-rails

Problem:
Get a single record from the contacts table where the first_name and last_name is equal to those given in the params. If there is more than one record found, then return the record that matches a domain.
def check_cache(params)
cached = where(first_name: params[:first_name], last_name: params[:last_name])
if cached.size > 1
# select the record with a matching one of params[:domains]
# cached #=> ['bob#gmail.com', 'bob#yahoo.com']
# params[:domains] #=> ['gmail.com', 'abc.com']
# result would be bob#gmail.com
end
cached
end
tried this in IRB
cached.select{|e| e =~ /(gmail.com)/}
but not sure how I would check each one in the params[:domains]

try this:
if cached.size > 1
params[:domains].each do |domain|
cached.select do |result|
result_domain = result.split("#").last
return result if result_domain == domain
end
end
end

Related

Setting default values for params in Rails 4 when the user leaves a form field blank

I have a DataPoint model and a form that allows the user to enter the age range and income range of the datapoints they want to see.
In data_points_controller.rb:
def result
#data_points = DataPoint.select_subset(params[:min_income], params[:max_income], params[:min_age], params[:max_age])
end
And in the model data_point.rb:
def self.select_subset(min_income, max_income, min_age, max_age)
DataPoint.where("annual_income BETWEEN :min_income AND :max_income AND age BETWEEN :min_age AND :max_age", {min_income: min_income, max_income: max_income, min_age: min_age, max_age: max_age})
end
This works fine when the user enters all 4 inputs: min_income, max_income, min_age and max_age. The correct subset of the data is returned with summary statistics.
However, when the user leaves any of the 4 inputs blank, I get an error:
Computation results to 'NaN'(Not a Number)
When a user omits a particular parameter, I'd like for the query to run as if there were no constraint on that particular parameter.
I took a look at the database and realized none of the ages are outside 0-100, so I tried:
def result
params[:min_age] ||= "0"
params[:max_age] ||= "100"
#data_points = DataPoint.select_subset(params[:min_income], params[:max_income], params[:min_age], params[:max_age])
end
But I am still getting the above-mentioned error.
Any ideas how I can get this working correctly even when the user doesn't fill out all 4 form fields?
Thanks!
Being not sure about presence of all of the passed attributes, you could rewrite your SQL query to include only those restrictions which have values from user:
def self.select_subset(min_income, max_income, min_age, max_age)
conditions = [].tap do |array|
array << "annual_income >= :min_income" if min_income.present?
array << "annual_income <= :max_income" if max_income.present?
array << "age >= :min_age" if min_age.present?
array << "age <= :max_age" if max_age.present?
end
named_params = {min_income: min_income, max_income: max_income, min_age: min_age, max_age: max_age}
DataPoint.where(conditions.join(' AND '), named_params)
end
Note that you should handle the case when user did not select any restriction at all somehow.
||= set a variable's value if its value were 'Nothing' (false or nil in Ruby)
However, if the params is blank ("") it will keep it blank.
a = ""
#=> ""
a ||= "foo"
#=> ""
So, try the following:
def result
params[:min_age] = "0" if params[:min_age].blank?
params[:max_age] = "100" if params[:max_age].blank?
#data_points = DataPoint.select_subset(params[:min_income], params[:max_income], params[:min_age], params[:max_age])
end
The blank? will cover nil and "".

Rails Search with query

I want to filter jobs on the parameter passed onto the model, currently search works flawlessly without query passed into the model, but when I type query it doesn't return anything. How can I perform this query with query and criteria.
results << model.with_query(query).where(criteria). any idea would be really appreciated.
module Refinery
class SearchEngine
# How many results should we show per page
RESULTS_LIMIT = 100
# Perform search over the specified models
def self.search(query, job_region, job_division, country, job_type, page = 1)
results = []
Refinery.searchable_models.each do |model|
criteria = {:job_region => job_region,
:job_division => job_division,
:country => country,
:job_type => job_type
}.select { |key, value| value.present? }
if query.present?
results << model.with_query(query).where(criteria)
else
results << model.limit(RESULTS_LIMIT).where(criteria)
end
end
results.flatten[0..(RESULTS_LIMIT - 1)]
end
end
end
The problem here is that the method .with_query(qry) returns an Array. You want to do chain-scoping, so you must use scopes that returns ActiveRecord::Relation objects.
model.with_query(query)
# returns an Array
model.with_query(query).where(criteria)
# calling .where on an Array object => NoMethodError
model.where(criteria)
# returns an ActiveRecord::Relation
model.where(criteria).with_query(query)
# calls the query on an AR::Relation object, which is doable
Short version: Change this:
results << model.with_query(query).where(criteria)
To this:
results << model.where(criteria).with_query(query)

Rails Array Conditions Query

I've written the following method to combine the References of Sections model and it's children:
def combined_references
ids = []
ids << self.id
self.children.each do |child|
ids << child.id
end
Reference.where("section_id = ?", ids)
end
But section.combined_references returns the following error:
Mysql2::Error: Operand should contain 1 column(s): SELECT `references`.* FROM `references` WHERE (section_id = 3,4)
It seems to have collected the correct values for ids, have I structured the query incorrectly?
Transform last line to:
Reference.where(section_id: ids)
and it should produce:
SELECT `references`.* FROM `references` WHERE section_id IN (3,4)
And you can shorten your code by one line with :
ids = []
ids << self.id
to
ids = [self.id]
it's invalid statement
WHERE (section_id = 3,4)
correct would be
WHERE (section_id in (3,4))
Please use:
Reference.where(:section_id => ids)
You can try something like this instead:
def combined_references
ids = self.children.map(&:id).push(self.id)
Reference.where(section_id: ids)
end
You can also query the database with:
Reference.where("section_id in (?)", ids)
The following has the most readability in my opinion:
def combined_references
Reference.where(section_id: self_and_children_ids)
end
private
def self_and_children_ids
self.children.map(&:id).push(self.id)
end

Unexpected syntax error

given the following code from my custom model:
def categorize
#cv = Cv.find(params[:cv_id], :include => [:desired_occupations, :past_occupations, :educational_skills])
#menu = :second
#language = Language.resolve(:code => :en, :name => :en)
categorizer = CvCategorizer.new #cv, #language
categorizer.prepare_occupations
categorizer.prepare_occupation_skills
categorizer.prepare_education_skills
# fetch the data
#occupation_hashes = categorizer.occupations
#skill_hashes = categorizer.skills
# Sort the hashes
#occupation_hashes.sort! { |x,y| y.score <=> x.score}
#skill_hashes.sort! { |x,y| y.score <=> x.score}
#max = #skill_hashes.first.score
#min = #skill_hashes.last.score
end
The code creates a new instance of the CvCategorizer class and calls the three prepare methods in sequence. They all do funky stuff with data retrieved from the database. The code looks as follows:
# = CvCategorizer
# This class will handle the categorizing of a CV based upon the skills and occupations found in
# the CV. Because the controller originally had a huge chunk of code, this class will break up that
# login into seperate function calls and keep everything inside variables for easy access.
class CvCategorizer
# initializes a new instance of the CvCategorizer
def initialize cv, language
#cv = cv
#language = language
#occupations = []
#skills = []
end
# Prepares the occupation array by looking at the stored CV and collecting
# all the desired occupations and past occupations. These are stored inside
# the internal occupation array as a uniq list.
def prepare_occupations
all_occupations = #cv.desired_occupations.all(:include => :labels) + #cv.past_occupations.all(:include => :labels)
all_occupations.each do |occupation|
oc = OccupationCategory.new
oc.is_desired_work?= #cv.desired_occupations.include?(occupation)
oc.is_work_experience?= #cv.past_occupations.include?(occupation)
oc.occupation = occupation
if !#occupations.include?(oc)
#occupations << oc
else
obj = #occupations.select(oc)
obj.score += 1
obj.occupations= (obj.occupations & oc).uniq
end
end
=begin
all_occupations = #cv.desired_occupations.all(:include => :labels) + #cv.past_occupations.all(:include => :labels)
all_occupations.each do |occupation|
section = []
section << "Desired Work" if #cv.desired_occupations.include? occupation
section << "Work experience" if #cv.past_occupations.include? occupation
unless (array = #occupations.assoc(occupation)).blank?
array[1]+= 1
array[2] = (array[2] & section).uniq
else
#occupations << [occupation, 1, section, []]
end
end
=end
end
# Prepares the occupation skills of the CV by looping over all the stored
# occupations and retrieving the skills for them and storing them in the
# skills array.
def prepare_occupation_skills
# Loop over all the OccupationCategory objects currently present in the Categorizer.
#occupations.each do |oc|
# For each OccupationCategory object, retrieve all the associated skills, and
# include their label as well.
oc.occupation.skills.all(:include => :labels).each do |skill|
# Get the label of the current concept we're working with.
label = oc.occupation.concept.label(#language).value
# Check if the #skills array already contains a SkillCategory object with the
# skill we're currently checking.
if (sc = #skills.select{|scs| scs.skill == skill}).blank?
# The skill was not found, so create a new entry with the SkillCategory class and set the
# correct values for the various properties
sc = SkillCategory.new
sc.labels << label
sc.score= 1
sc.is_occupation_skill? = true
sc.skill= skill
else
# The skill was found in one of the SkillCategory objects. So we update the score by
# 1 and store the label of the current concept, unless that label is already present.
sc.labels << label unless sc.labels.include?(label)
sc.is_occupation_skill?= true
sc.score+= 1
end
end
end
=begin
#occupations.each do |array|
array[0].skills.all(:include => :labels).each do |skill|
unless (skill_array = #skills.assoc skill).blank?
label = array[0].concept.label(#language).value
skill_array[1]+= 1
skill_array[3] << label unless skill_array[3].include? label
else
#skills << [skill, 1, [], [array[0].concept.label(#language).value]]
end
end
end
=end
end
# Prepares the education skills by checking the CV and adding them to the
# skills array
def prepare_education_skills
# Loop over all the educational skills that are currently associated to the CV.
#cv.educational_skills.all(:include => :labels).each do |skill|
# Check if the #skills array already contains a SkillCategory object with the
# skill we're currently checking.
if (sc = #skills.select{|scs| scs.skill == skill}).blank?
# The skill was not found, so create a new entry with the SkillCategory class and set the
# correct values for the various properties
sc = SkillCategory.new
sc.labels << 'Education skills' unless sc.labels.include?('Education skills')
sc.score= 1
sc.is_educational_skill?= true
sc.skill= skill
else
# The skill was found in one of the SkillCategory objects. So we update the score by
# 1 and store the label of the current concept, unless that label is already present.
sc.labels << 'Education skills' unless sc.labels.include?('Education skills')
sc.is_educational_skill?= true
sc.score+= 1
end
end
=begin
#cv.educational_skills.all(:include => :labels).each do |skill|
unless (array = #skills.assoc skill).blank?
array[1]+= 1
array[3] << 'Education skills' unless array[3].include? 'Education skills'
else
#skills << [skill, 1, ['Education skills'], []]
end
end
=end
end
# Returns all uniq skills with their score and section found.
# array structure for each element
# - 0 : the skill object
# - 1 : the score for the skill
# - 2 : CV location of the skill
# - 3 : ESCO group of the skill
def skills
#skills
end
# Returns all uniq occupations with their score and section found.
# array structure for each element
# - 0 : the occupation object
# - 1 : the score for the occupation
# - 2 : the CV location of the occupation
# - 3 : empty array for occupations
def occupations
#occupations
end
end
When browsing to the relevant view in the application, i'm receiving the following error message from the server:
/home/arne.de.herdt/RubymineProjects/ESCO/app/models/cv_categorizer.rb:21:
syntax error, unexpected tIVAR, expecting kEND
oc.is_desired_work?= #cv.desired_occupations.include?(...
^
/home/arne.de.herdt/RubymineProjects/ESCO/app/models/cv_categorizer.rb:22:
syntax error, unexpected tIVAR, expecting kEND ...
oc.is_work_experience?= #cv.past_occupations.include?(occ...
^
/home/arne.de.herdt/RubymineProjects/ESCO/app/models/cv_categorizer.rb:69:
syntax error, unexpected '=', expecting kEND
sc.is_occupation_skill? = true
^
/home/arne.de.herdt/RubymineProjects/ESCO/app/models/cv_categorizer.rb:75:
syntax error, unexpected kTRUE, expecting kEND
/home/arne.de.herdt/RubymineProjects/ESCO/app/models/cv_categorizer.rb:108:
syntax error, unexpected kTRUE, expecting kEND
/home/arne.de.herdt/RubymineProjects/ESCO/app/models/cv_categorizer.rb:114:
syntax error, unexpected kTRUE, expecting kEND
It seems I'm missing something in the CvCategorize class, but I can't find the stuff missing. The IDE is not showing errors such as missing ends or anything.
Remove the question marks on oc.is_desired_work? and oc.is_work_experience? on lines 21 and 22.
ruby allows the question marks in method names, not the variables ( of any kind ) or object attributes.
the ruby way would be to add instance methods to your OccupationCategory class, like so:
class OccupationCategory
def is_desired_work?
...
end
def is_work_experience?
...
end
end
so you could later use it like
oc.is_desired_work?
oc.is_work_experience?

Removing “duplicate objects” with same attributes using Array.map

As you can see in the current code below, I am finding the duplicate based on the attribute recordable_id. What I need to do is find the duplicate based on four matching attributes: user_id, recordable_type, hero_type, recordable_id. How must I modify the code?
heroes = User.heroes
for hero in heroes
hero_statuses = hero.hero_statuses
seen = []
hero_statuses.sort! {|a,b| a.created_at <=> b.created_at } # sort by created_at
hero_statuses.each do |hero_status|
if seen.map(&:recordable_id).include? hero_status.recordable_id # check if the id has been seen already
hero_status.revoke
else
seen << hero_status # if not, add it to the seen array
end
end
end
Try this:
HeroStatus.all(:group => "user_id, recordable_type, hero_type, recordable_id",
:having => "count(*) > 1").each do |status|
status.revoke
end
Edit 2
To revoke the all the latest duplicate entries do the following:
HeroStatus.all(:joins => "(
SELECT user_id, recordable_type, hero_type,
recordable_id, MIN(created_at) AS created_at
FROM hero_statuses
GROUP BY user_id, recordable_type, hero_type, recordable_id
HAVING COUNT(*) > 1
) AS A ON A.user_id = hero_statuses.user_id AND
A.recordable_type = hero_statuses.recordable_type AND
A.hero_type = hero_statuses.hero_type AND
A.recordable_id = hero_statuses.recordable_id AND
A.created_at < hero_statuses.created_
").each do |status|
status.revoke
end
Using straight Ruby (not the SQL server):
heroes = User.heroes
for hero in heroes
hero_statuses = hero.hero_statuses
seen = {}
hero_statuses.sort_by!(&:created_at)
hero_statuses.each do |status|
key = [status.user_id, status.recordable_type, status.hero_type, status.recordable_id]
if seen.has_key?(key)
status.revoke
else
seen[key] = status # if not, add it to the seen array
end
end
remaining = seen.values
end
For lookups, always use Hash (or Set, but here I thought it would be nice to keep the statuses that have been kept)
Note: I used sort_by!, but that's new to 1.9.2, so use sort_by (or require "backports")

Resources