Reputation: 35
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?
Upvotes: 0
Views: 769
Reputation: 21130
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.
Upvotes: 0
Reputation: 168101
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
Upvotes: 0
Reputation: 2450
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:
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)
Upvotes: 3