Checking multiple params in Ruby - ruby

These params come out of html inputs in erb templates (this code is in the main application.rb), and I am checking if they are filled before I add them to n.requestusers, which will become part of a database entry. It works, but it feels more like a bash script the way it is now. What would be the best way to write something like this?
a route in the main .rb
if params[:user2].empty? && params[:user3].empty? && params[:user4].empty? && params[:user5].empty?
n.requestusers = params[:user1]
elsif params[:user3].empty? && params[:user4].empty? && params[:user5].empty?
n.requestusers = params[:user1], params[:user2]
elsif params[:user4].empty? && params[:user5].empty?
n.requestusers = params[:user1], params[:user2], params[:user3]
elsif params[:user5].empty?
n.requestusers = params[:user1], params[:user2], params[:user3], params[:user4]
else
n.requestusers = params[:user1], params[:user2], params[:user3], params[:user4], params[:user5]
end

Instead of having all of those conditional statements might you be interested in something like:
n.requestusers = params.select { |key, val| not val.empty? }.values
Or a cleaner way as suggested by #theTinMan:
n.requestusers = params.reject { |key, val| val.empty? }.values
select lets you take all of the none empty parameter values and returns them. values lets you grab those values as an array.
I am not experienced with web frameworks, so my suggestion is a bit of a shot in the dark.

This isn't tested, because there are no sample values to test against, but, after some refactoring I have:
if [:user2, :user3, :user4, :user5].all?{ |s| params[s].empty? }
n.requestusers = params[:user1]
elsif [:user3, :user4, :user5].all? { |s| params[s].empty? }
n.requestusers = [:user1, :user2].map{ |s| params[s] }
elsif [:user4, :user5].all? { |s| params[s].empty? }
n.requestusers = [:user1, :user2, :user3].map{ |s| params[s] }
elsif params[:user5].empty?
n.requestusers = [:user1, :user2, :user3, :user4].map{ |s| params[s] }
else
n.requestusers = [:user1, :user2, :user3, :user4, :user5].map{ |s| params[s] }
end
Looking at that further, this seems sensible:
USER_LIST = [:user1, :user2, :user3, :user4, :user5]
USER_LIST.size.times do |i|
user_list = USER_LIST
get_users = user_list.shift(1 + i)
if user_list.all?{ |s| params[s].empty? }
n.requestusers = params.values_at(get_users)
break
end
end
Like I said, that's not tested, but I'd work with something along those lines.
Adjust USER_LIST as necessary.

I am not that familiar with Sinatra, but if you wanted to add elements to the array, you can just do (assuming n.requestusers has already been initialized):
n.requestusers << params[:user1] unless params[:user1].empty?
You can do that for each user parameter.
Edit: It would probably be better just to check if the param exists since if it isn't found, nil is returned -- calling empty? on nil throws a NoMethodError. It would probably better to do:
n.requestusers << params[:user1] unless params[:user1]
This would ensure that it adds the param if it exists.

Related

how to turn off Cyclomatic complexity for where is too high

I have a where method in model that's throwing lint error. The whole code in model is just a test code at this moment and will be refactored later on. So i want to turn off this lint error for now.
UPDATE:
Here's the method i am getting lint error at
def self.where(start_date, end_date, customer_id, type, location, is_registered)
filtered_data = if start_date && end_date
customers.select do |e|
e[:startDateTime].to_datetime >= start_date.to_datetime &&
e[:endDateTime].to_datetime <= end_date.to_datetime
end
elsif start_date
customers.select {|e| e[:startDateTime].to_datetime >= start_date.to_datetime }
elsif end_date
customers.select {|e| e[:endDateTime].to_datetime <= end_date.to_datetime }
else
customers
end
if !is_registered.nil? # is_registered is true or false
filtered_data = customers.select { |e| e[:isRegistered].to_s == is_registered }
end
# Check if hash presents and check if the keys have valid values.
if customer_id || type || location
hash = { customerId: customer_id.to_i, type: type, location: location }
# delete if type, location or customer_id is nil.
hash = hash.delete_if { |_k, v| v.nil? || v == 0 }
keys = hash.keys
filtered_data = filtered_data.select { |h| h.select { |k| keys.include?(k) } == hash }
else
filtered_data
end
filtered_data.map do |slot|
mock_customer(slot[:id], slot[:customerId], slot[:name], slot[:startDateTime],
slot[:endDateTime], slot[:location], slot[:status])
end
end
I tried adding # rubocop:disable Metrics/AbcSize in model but didnt help.
Try this:
# rubocop:disable Metrics/CyclomaticComplexity
.... your method here
# rubocop:enable Metrics/CyclomaticComplexity
Also, if you want to turn off Rubocop for all those test files (since you are going to refactor them), you can try this answer.
This works for me
# rubocop:disable Style/CyclomaticComplexity
yor code
# rubocop:enable Style/CyclomaticComplexity

Find the first differing character between two Strings in Ruby

I have two strings.
str_a = "the_quick_brown_fox"
str_b = "the_quick_red_fox"
I want to find the first index at which the two strings differ (i.e. str_a[i] != str_b[i]).
I know I could solve this with something like the following:
def diff_char_index(str_a, str_b)
arr_a, arr_b = str_a.split(""), str_b.split("")
return -1 unless valid_string?(str_a) && valid_string?(str_b)
arr_a.each_index do |i|
return i unless arr_a[i] == arr_b[i]
end
end
def valid_string?(str)
return false unless str.is_a?(String)
return false unless str.size > 0
true
end
diff_char_index(str_a, str_b) # => 10
Is there a better way to do this?
Something like this ought to work:
str_a.each_char.with_index
.find_index {|char, idx| char != str_b[idx] } || str_a.size
Edit: It works: http://ideone.com/Ttwu1x
Edit 2: My original code returned nil if str_a was shorter than str_b. I've updated it to work correctly (it will return str_a.size, so if e.g. the last index in str_a is 3, it will return 4).
Here's another method that may strike some as slightly simpler:
(0...str_a.size).find {|i| str_a[i] != str_b[i] } || str_a.size
http://ideone.com/275cEU
i = 0
i += 1 while str_a[i] and str_a[i] == str_b[i]
i
str_a = "the_quick_brown_dog"
str_b = "the_quick_red_dog"
(0..(1.0)/0).find { |i| (str_a[i] != str_b[i]) || str_a[i].nil? }
#=> 10
str_a = "the_quick_brown_dog"
str_b = "the_quick_brown_dog"
(0..(1.0)/0).find { |i| (str_a[i] != str_b[i]) || str_a[i].nil? }
#=> 19
str_a.size
#=> 19
This uses a binary search to find the index where a slice of str_a no longer occurs at the beginning of str_b:
(0..str_a.length).bsearch { |i| str_b.rindex(str_a[0..i]) != 0 }

Compare two hash arrays in ruby except a key

I have two hash arrays like this:
hashArray1 = [{"id"=>"1","data"=>"data1"},{"id"=>"2","data"=>"data2"}]
hashArray2 = [{"id"=>"3","data"=>"data1"},{"id"=>"4","data"=>"data2"}]
I want to compare both of them and return true if everything else matches without the "id" key.
I have tried something like this:
hashArray1.each do |h1|
hashArray2.each do |h2|
if h1.select{|h| h!= "id"} == h2.select{|b| b!= "id"}
break
else
return false
end
end
end
But this seems to be incorrect. Does anyone have a better solution. I am on plain ruby 1.9.3, not using rails framework.
I would simply do:
hash1.zip(hash2).all? do |h1,h2|
return false unless h1.keys == h1.keys
h1.keys.each do |key|
return false if h1[key] != h2[key] unless key == 'id'
end
end
If hash1.length != hash2.length then you can bail out immediately as they can't be the same. If they have the same length then you could do something like this:
except_id = ->(h) { h.reject { |k, v| k == 'id' } }
same = hash1.zip(hash2).find { |h1, h2| except_id[h1] != except_id[h2] }.nil?
If same is true then they're the same (while ignore 'id's), otherwise they're different. Using Hash#reject is one pure Ruby way to non-destructively look at the Hash without a particular key. You could also use:
except_id = lambda { |h| h = h.dup; h.delete('id'); h }
if "copy and remove" makes more sense to you than filtering. If you don't like find then all? might read better:
same = hash1.zip(hash2).all? { |h1, h2| except_id[h1] == except_id[h2] }
or even:
same_without_id = lambda { |h1, h2| except_id[h1] == except_id[h2] }
same == hash1.zip(hash2).all?(&same_without_id)
The question is not necessarily clear, but I assume the order of the hashes are taken into account.
hash1.map{|h| h.reject{|k, _| k == "id"}} ==
hash2.map{|h| h.reject{|k, _| k == "id"}}

Cleanest way to loop over master set, processing each sub-set, in Ruby

This is a common scenario and I'm never quite happy with the solutions. You have a set of data, just assume rows from a db ordered by category in this case.
You want to build a buffer with a sub-set of each category, and on each category change, do some processing, then clear the buffer. Assume that process() is wrapped in a bunch of complex logic that you don't want to duplicate
Anyone have a better setup?
# category, city
data = [
[10, 'citya'],
[10, 'cityb'],
[11, 'citya'],
[11, 'cityb'],
[11, 'citya'],
[12, 'cityb'],
[12, 'cityg']
]
# do some heavy lifting in here
def process(buf) p buf; end
cur_cat = nil
cur_cat_buf = []
data.each do |r|
if r[0] != cur_cat
cur_cat = r[0]
process(cur_cat_buf) #<-- assume this is conditional, complex
cur_cat_buf.clear
end
cur_cat_buf << r[1]
end
process(cur_cat_buf) #<-- assume the conditional is duplicated...ack.
This is the other technique, and is just terrible. Messy, awful! Always looking ahead, checking if it is nil or different etc...ugh...
cur_cat = data[0][0] if data.length > 0
cur_cat_buf = []
data.each_with_index do |r, i|
cur_cat_buf << r[1]
# look ahead
if data[i+1] == nil or data[i+1][0] != cur_cat
cur_cat = data[i+1][0] if data[i+1] != nil
process(cur_cat_buf)
cur_cat_buf.clear
end
end
This is another alternative. Certainly better than the last one.
cur_cat = nil
cur_cat_buf = []
for i in 0..(data.length)
if (r = data[i]) == nil or r[0] != cur_cat
process(cur_cat_buf)
break unless r
cur_cat_buf.clear
cur_cat = r[0]
end
cur_cat_buf << r[1]
end
I want a clean, elegant solution. There's gotta be a better way!
data.group_by(&:first).each_value {|buffer| process(buffer.map &:last) }
data.group_by(&:first).each_value do |pairs|
process(pairs.map(&:last))
end
Or the equivalent, yet slightly more verbose, yet slightly more explicit:
data.group_by { |category_id, city| category_id }.each_value do |pairs|
process(pairs.map { |category_id, cities| cities })
end

Array select with multiple conditions ruby

I can do:
#items = #items.select {|i| i.color == 'blue'}
#items = #items.select {|i| i.color == 'blue' || i.color == 'red'}
What if I am given an unknown amount of colors and I want to select them all? i.e.
['red','blue','green','purple']
# or
['blue','red']
I've been working on a mess of code that creates several temporary arrays and then merges or flattens them into one, but I'm really unhappy with it.
Try this:
colors = ['red','blue','green','purple']
#items = #items.select { |i| colors.include?(i.color) }
You might also want to consider this instead, for in-place changes:
#items.reject! { |i| !colors.include?(i.color) }
not sure I fully understand your question but would work for you?
colors_array = ['blue','red','whatever']
#items = #items.select {|i| colors_array.include?(i)}

Resources