autocomplete through Array of strings in Rails 4 - ruby-on-rails

I'm using this gist to build autocomplete functionality in my Rails app.
I'm saving record in Shoe model attribute like below
"nike air, nike steam,nike softy ,nike strength" #comma separated words
My controller code is below
def shoes
shoes_list = []
shoes = Shoe.all
shoes.each do |shoe|
shoes_list << shoe.model.split(',')
end unless shoes.blank?
if params[:term]
like = "%".concat(params[:term].concat("%"))
# shoes = Shoe.where("model like ?", like)
# **How i make like query to "shoes_list" same like above commented line?**
else
shoes = Shoe.all
end
list = shoes.map { |u| Hash[id: u.id, label: u.model, model: u.model] }
render json: list
end
How do I render it in json format?

At last this code works for me.
def shoes
shoes_list = []
shoes = Shoe.all
shoes.each do |shoe|
shoes_list << shoe.model.split(',')
end unless shoes.blank?
shoes_list.flatten!
if params[:term]
shoes = shoes_list.grep(Regexp.new( Regexp.escape(params[:term]), "i" ))
else
shoes = shoes_list
end
list = shoes.map {|u| Hash[id: u, label: u, name: u]}
render json: list
end
Also see How get value from array of strings in Ruby 2.0

Related

Clean up messy code that query's based on multiple options

I'm using Rails, but the underlying question here applies more broadly. I have a report page on my web app that allows the user to specify what they're filtering on, and query the database based on those filters (MongoDB).
The data is based around hotels, the user must first select the regions of the hotels (state_one, state_two, state_three), then the statuses of the hotels (planning, under_construction, operational), then an optional criteria, price range (200, 300, 400). Users can select multiple of each of these options.
My way of doing this currently is to create an empty array, iterate through each region, and push the region into the array if the user selected that region. Then, I'm iterating through THAT array, and assessing the status of the hotels in those regions, if any hotel has the status the user has selected, then I'm adding that hotel to a new empty array. Then I do the same thing for price range.
This works, but the code is offensively messy, here's an example of the code:
def find_hotel
hotels = find_all_hotels
first_array = []
hotels.each do |hotel|
if params[:options][:region].include? 'state_one' and hotel.state == :one
first_array.push(hotel)
elsif params[:options][:region].include? 'state_two' and hotel.state == :two
first_array.push(hotel)
elsif params[:options][:region].include? 'state_three' and hotel.state == :three
first_array.push(hotel)
end
end
second_array = []
first_array.each do |hotel|
if params[:options][:region].include? 'planning' and hotel.status == :planning
first_array.push(hotel)
elsif params[:options][:region].include? 'under_construction' and hotel.status == :under_construction
first_array.push(hotel)
elsif params[:options][:region].include? 'operational' and hotel.status == :operational
first_array.push(hotel)
end
end
third_array = []
second_array.each do |hotel|
# More of the same here, this could go on forever
end
end
What are some better ways of achieving this?
How about this:
STATES = [:one, :two, :three]
STATUSES = [:planning, :under_construction, :operational]
PRICES = [200, 300, 400]
def find_hotel
region = params[:options][:region]
first_array = set_array(region, find_all_hotels, STATES, :state)
second_array = set_array(region, first_array, STATUSES, :status)
third_array = set_array(region, second_array, PRICES, :price_range)
end
def set_array(region, array, options, attribute)
array.each_with_object([]) do |element, result|
options.each do |option|
result << element if region.include?(option) && element[attribute] == option
end
end
end
UPDATE
Added attribute parameter to set_array in order to make the code work with your updated example.
Since second_array is empty, whatever you get by iterating over it (perhaps third_array) would also be empty.
def find_hotel
hotels = find_all_hotels
first_array = hotels
.select{|hotel| params[:options][:region].include?("state_#{hotel.state}")}
first_array += first_array
.select{|hotel| params[:options][:region].include?(hotel.status.to_s)}
second_array = third_array = []
...
end

Ruby - Extract value of a particular key from array of hashes

I have an array of hashes - #profiles which has data as:
[{:user_id=>5, :full_name=>"Emily Spot"},{:user_id=>7, :full_name=>"Kevin Walls"}]
I want to get full_name of say user_id = 7? I'm doing the following: but it's throwing an error that expression #profiles.find{|h| h[':user_id'] == current_user.id} is nil.
name = #profiles.find{ |h| h[':user_id'] == current_user.id }[':full_name']
if I use select instead of find then error is - no implicit conversion of String into Integer.
How do I search through the array of hashes?
UPDATE:
After #Eric's answer, I restructured my job model & view actions:
def full_names
profile_arr||= []
profile_arr = self.applications.pluck(:user_id)
#profiles = Profile.where(:user_id => profile_arr).select([:user_id, :first_name, :last_name]).map {|e| {user_id: e.user_id, full_name: e.full_name} }
#full_names = #profiles.each_with_object({}) do |profile, names|
names[profile[:user_id]] = profile[:full_name]
end
end
In the view....,
p #current_job.full_names[current_user.id]
#profiles is an array of hashes, with symbols as keys, whereas what you use is String objects.
So ':user_id' is a string, and you want symbol: :user_id:
#profiles.find{ |h| h[:user_id] == current_user.id }
I want to get full_name of say user_id == 7
#profiles.find { |hash| hash[:user_id] == 7 }.fetch(:full_name, nil)
Note, I used Hash#fetch for case, when there is no hash with value 7 at key :user_id.
As you've noticed, it's not very convenient to extract the name of user_id 7. You could modify your data structure a bit :
#profiles = [{:user_id=>5, :full_name=>"Emily Spot"},
{:user_id=>7, :full_name=>"Kevin Walls"}]
#full_names = #profiles.each_with_object({}) do |profile, names|
names[profile[:user_id]] = profile[:full_name]
end
p #full_names
# {5=>"Emily Spot", 7=>"Kevin Walls"}
p #full_names[7]
# "Kevin Walls"
p #full_names[6]
# nil
You didn't lose any information but name look-up is now much faster, easier and more robust.
Suggesting, to create a new hash that can make things simpler
Eg:
results = {}
profiles = [
{user_id: 5, full_name: "Emily Spot"},
{user_id: 7, full_name: "Kevin Walls"}
]
profiles.each do |details|
results[details[:user_id]] = details[:full_name]
end
Now, results will have:
{5: "Emily Spot", 7: "Kevin Walls"}
So, if you need to get full_name of say user_id = 7, simply do:
results[7] # will give "Kevin Walls"

id nil when select model without no relation

I have a model with no relation.
class GridConfig < ActiveRecord::Base
end
my question is why result of query are appended id:nil columns?
GridConfig.select(:fontSize)
result is
#<GridConfig id: nil, fontSize: "12px">
is there any options for this?
thank you.
I want find some records and pick certain columns. and send to client.
user_key = params[:user_key]
grid_id = params[:grid_id]
#config = GridConfig.where(['user_key = ? and grid_id = ?', user_key, grid_id])
.select(:model_id, :fontSize, :displayCount, :columnModel)
# i checked #config variables at this point and found nil:id...
#config = #config.index_by(&:model_id)
# and i want to this makes indexed by model_id like [{"model":{...}},{"model2" : {...}}, {}]
respond_to do |format|
format.json { render json: #config }
end
You can use the pluck method to select only certain columns into an array, then index by the first column.
#config = GridConfig.where(user_key: params[:user_key], grid_id: params[:grid_id])
.pluck(:model_id, :fontSize, :displayCount, :columnModel)
#config = #config.index_by{ |x| x[0] }

Repacking query result from multiple models. Rails-way

I have a controller that renders json. Here's code:
class AppLaunchDataController < ApiController
def index
service_types = []
vendors = []
tariffs = []
fields = []
vendors_hash = {}
service_types_hash = {}
tariffs_hash = {}
fields_hash = {}
#service_types = ServiceType.select("title, id").all.each do |service_type|
service_types_hash = {id: service_type.id, title: service_type.title}
service_types << service_types_hash
#vendors = service_type.vendors.select("title, id").all.each do |vendor|
vendors_hash = {id: vendor.id, title: vendor.title}
vendors << vendors_hash
#tariff = vendor.tariffs.select("title, id").all.each do |tariff|
tariffs_hash = {id: tariff.id, title: tariff.title}
tariffs << tariffs_hash
#fields = tariff.fields.select("id, current_value, value_list").all.each do |field|
fields_hash = {id: field.id, current_value: field.current_value, value_list: field.value_list}
fields << fields_hash
end
tariffs_hash[:fields] = fields
fields = []
end
vendors_hash[:tariffs] = tariffs
tariffs = []
end
service_types_hash[:vendors] = vendors
vendors = []
end
render json: service_types
end
end
Return value looks like this:
[{"id":1,"title":"Water",
"vendors":[{"id":1,"title":"Vendor_1",
"tariffs":[{"id":1,"title":"Unlim",
"fields":[{"id":1,"current_value":"200","value_list":null},{"id":2,"current_value":"Value_1","value_list":"Value_1, Value_2, Value_3"}]},{"id":2,"title":"Volume",
"fields":[]}]},
{"id":2,"title":"Vendor_2",
"tariffs":[]}]},
{"id":2,"title":"Gas",
"vendors":[]},
{"id":3,"title":"Internet",
"vendors":[]}]
It works, but I'm sure there's another (more rails-) way to get the result.
If anyone dealt with it before, please help. Thanks.
just use
# for eager-loading :
#service_types = ServiceType.includes( vendors: {tariffs: :fields} )
# now for the json :
#service_types.to_json( include: {vendors: {include: {tariffs: { include: :fields}}}} )
if your ServiceType object will always have this kind of representation, just override the model's as_json method:
class ServiceType
def as_json( options={} )
super( {include: :vendors }.merge(options) ) # vendors, etc.
end
end
this is encouraged way to do it in rails : calling to_json on the model will just call as_json, possibly with additional options. In fact, as_json describes the canonical json representation for this model. See the api dock on to_json for more insight.
If your needs are more peculiar ( as using selects for a faster query ), you can always roll your own to_json_for_app_launch_data method on the model (using or not as_json), or even better on a presenter

Rails - Fetch results on the basis of number of params in query string

I am working on an events application where i want to filter events depending on the 3 parameters location or starts_at or ends_at in the query string. There can be any one, two or all the parameters in the query string. In i use if-else statement i need to make 6 cases which will make my code clumsy. Rather i am thinking to implement something this way:
class EventsController < ApplicationController
def index
unless params.empty?
unless params[:location].nil?
#events = Event.where("location = ?", params[:location])
end
unless params[:starts_at].nil?
unless #events.empty?
#events = #events.where("start_date = ?", params[:start_date])
else
#events = Event.where("Date(starts_at) = Date(?)", params[:starts_at])
end
end
unless params[:ends_at].nil?
unless #events.empty?
#events = #events.where("end_date = ?", params[:end_date])
else
#events = Event.where("Date(ends_at) = Date(?)", params[:ends_at])
end
end
end
end
end
But this code doesnt work since where query doen not work on an array. Can someone suggest me some solution for this..
You should be able to pass your params hash directly to where, and it will form the correct SQL based on the keys and values of that hash:
Event.where(params)
An example in the console:
1.9.3p194 :001 > puts Example.where(:location => 'here', :started_at => '2012-08-13').to_sql
SELECT "examples".* FROM "examples" WHERE "examples"."location" = 'here' AND "examples"."started_at" = '2012-08-13'
Try Following
def index
unless params.empty?
where_array, arr = [], []
if params[:location]
where_array << "location = ?"
arr << params[:location]
end
if params[:starts_at]
where_array << "start_date = ?"
arr << params[:starts_at]
end
if params[:ends_at]
where_array << "end_date = ?"
arr << params[:ends_at]
end
#events = arr.blank? ? [] : Event.where([where_array.join(" AND "), *arr])
end
end

Resources