Sort json data alphabetically in ruby on rails - ruby-on-rails

I have this data with me:
[{:id=>250,
:application_date=>"02/04/2016",
:customer_number=>"",
:customer_name=>"Neymar Silva Junior",
:city=>"Auckland",
:region=>"Auckland",
:service=>"Electricity",
:name=>"Bosco and Sons",
:service_plan=>"Electricity Plan",
:connection_type=>nil,
:billing_method=>nil,
:icp_number=>nil,
:moving_date=>"",
:supplier_commission=>21.0,
:show_url=>"/applications/250"},
{:id=>257,
:application_date=>"27/05/2016",
:customer_number=>"",
:customer_name=>"Ariel name Parra",
:city=>"Dunedin",
:region=>"Dunedin",
:service=>"Electricity",
:name=>"Bosco and Sons",
:service_plan=>"Electricity Plan",
:connection_type=>nil,
:billing_method=>nil,
:icp_number=>nil,
:moving_date=>"28/05/2016",
:supplier_commission=>21.0,
:show_url=>"/applications/257"},
{:id=>291,
:application_date=>"29/04/2016",
:customer_number=>"aaaa",
:customer_name=>"Neymar Silva Junior",
:city=>"Auckland",
:region=>"Auckland",
:service=>"Electricity",
:name=>"Bosco and Sons",
:service_plan=>"Electricity Plan",
:connection_type=>nil,
:billing_method=>nil,
:icp_number=>"",
:moving_date=>"",
:supplier_commission=>28.0,
:show_url=>"/applications/291"},
{:id=>292,
:application_date=>"29/04/2016",
:customer_number=>"23223",
:customer_name=>"Neymar Silva Junior",
:city=>"Auckland",
:region=>"Auckland",
:service=>"Electricity",
:name=>"Bosco and Sons",
:service_plan=>"Electricity Plan",
:connection_type=>nil,
:billing_method=>nil,
:icp_number=>"",
:moving_date=>"",
:supplier_commission=>21.0,
:show_url=>"/applications/292"}]
I want to sort this data in two different ways, alphabetically(from A to Z) as well as Recursively(Z to A) according to its attributes in following scenarios:
If the sort parameter is service_plan alphabetically it will sort as per this attribute from A to Z, if recursively then Z to A for this attribute and so on for all attributes.
Id is integer so it should be sorted in increasing or decreasing order.
Moreover the nil value should not through an error and should be present in the result.
Thanks in advance!

def my_sort(data, attribute, asc = true)
# Make sure that all elements have attribute we want the data to be sorted by
return data unless data.all? { |elem| elem.key?(attribute) }
sorted = data.sort_by { |elem| elem[attribute] }
asc ? sorted : sorted.reverse
end
my_sort(data, :id) # ascending order by default
my_sort(data, :city, false)
If you want to sort by element that can be missing:
def my_sort(data, attribute, asc = true)
# Convert to string because of possible nil values
sorted = data.sort_by { |elem| elem[attribute].to_s }
asc ? sorted : sorted.reverse
end

Related

Optimize with joins, order, distinct on current active records / sql query

I am using ruby on rails 4 and postgres currently, and making distinct and sorting as in the code below.
I just have a feeling is not right and not optimized because it seems to be doing all this with an array.
How could I combine this all into one activerecords query ?
ads1 = Advertisement.where(property_id: nil)
.where(target_address_city: [property.address_city, ""],
target_address_state: [property.address_state, ""],
target_address_country_id: property.address_country_id)
# return advertisement belongs to the property
ads2 = management.property.advertisements
#combine both results
combine_ads = ads1 + ads2
#remove ads duplicate
uniq_ads = combine_ads.uniq { |ads| ads.id}
# sort by created_at desc
uniq_ads = uniq_ads.sort_by { |ads| -ads[:id]}
# do pagination
final_ads = uniq_ads.paginate(:page => params[:page], :per_page => params[:page_limit])

Ruby sort_by multiple fields which may include nil values

I want to sort an array of hashes in which each hash has two attributes created_time and updated_time. And the value of these attributes may be either a datestring or nil. I want it to sort by created_time(ASC) first and updated_time(ASC).I tried with sort_by method and I could not specify the condition for multiple keys like sort method suggested in this discussion .
The two attributes created_time and updated_time should be sorted in ascending order with null values as last
Suppose if the array is
[
{"created_time" => nil, "updated_time" => "2016-04-10"},
{"created_time" => nil, "updated_time" => "2016-04-09"},
{"created_time" => "2016-04-15", "updated_time" => nil}
]
I want the result as
[
{"created_time"=>"2016-04-15", "updated_time"=>nil},
{"created_time"=>nil, "updated_time"=>"2016-04-09"},
{"created_time"=>nil, "updated_time"=>"2016-04-10"}
]
What to do?
Sorry for the lack of clarity in the question. I just wanted to sort an array of hashes with two keys in ascending order which can have nil values.And I figured out the way
def date_time datestring
DateTime.parse(datestring)
end
result = array_to_sort.sort do |a,b|
(a["created_time"] && b["created_time"] && a["updated_time"] && b["updated_time"]) ? [ date_time(a["created_time"]), date_time(a["updated_time"])] <=> [ date_time(b["created_time"]), date_time(b["updated_time"])] : [ a["created_time"] && b["created_time"] ? date_time(a["created_time"]) : a["created_time"] ? -1 : 1, a["updated_time"] && b["updated_time"] ? date_time(a["updated_time"]) : a["updated_time"] ? -1 : 1] <=> [ a["created_time"] && b["created_time"] ? date_time(b["created_time"]) : b["created_time"] ? -1 : 1, a["updated_time"] && b["updated_time"] ? date_time(b["updated_time"]) : b["updated_time"] ? -1 : 1]
end
Suppose arr is an array of hashes {"created_time"=>ct, "updated_time"=>ut}, where ct (ut) is either a datestring with fomat " yyyy-mm-dd" or nil. Let far_away_time be a datestring that is guaranteed to be later than any datestring value of ct or ut. For example, far_away_time = "3000-01-01". Then you can sort using Enumerable #sort_by as follows:
arr.sort_by do |h|
ct, ut = h.values
case [ct.nil?, ut.nil?]
when [true, true] then [far_away_time, far_away_time, far_away_time]
when [true, false] then [far_away_time, far_away_time, ut]
when [false, true] then [far_away_time, ct, far_away_time]
else [ct, ut, ""]
end
end
Since the datestrings are in ISO 8601 format, there is no need to convert them to Date objects (i.e., the strings themselves can be sorted). For example, "2016-03-12" < "2016-03-13" #=> true. See the doc for Array#<=> for an explanation of how arrays are ordered.
I am sorting be a three-element array in order that the hashes {"created_time"=>"2016-01-01", "updated_time"=>nil}, {"created_time"=>nil, "updated_time"=>"2016-01-01"} and {"created_time"=>nil, "updated_time"=>nil} are ordered as I have listed them (and placed at the end of the sorted array).

Supply Ruby Array Select A Dynamic Block

I have an array of hashes. Here is a small sample of the typical values:
[{"id"=>1,
"context"=>"r178",
"asset"=>"Art Schools Hub",
"campaign"=>"Fashion Careers",
"contact_email"=>"evert_nolan#hammechaefer.net",
"notes"=>"",
"user_first_name"=>"Agustin",
"user_last_name"=>"Welch",
"status"=>"Completed",
"date_collected"=>"01/22/16"},
{"id"=>4,
"context"=>"r178",
"asset"=>"Art Schools Hub",
"campaign"=>"Graphic Design Careers",
"contact_email"=>"jamil_brakus#effertz.biz",
"notes"=>"",
"user_first_name"=>"Agustin",
"user_last_name"=>"Welch",
"status"=>"In Progress",
"date_collected"=>"01/22/16"},
{"id"=>15,
"context"=>"r178",
"asset"=>"Art Schools Hub",
"campaign"=>"Art Education",
"contact_email"=>"miss_kyle_mccullough#hicklezboncak.net",
"notes"=>"",
"user_first_name"=>"Jermaine",
"user_last_name"=>"Wilkinson",
"status"=>"Open",
"date_collected"=>"01/22/16"}]
I know that doing a select like this:
results = #network.select { |x| x["campaign"] == "Art Education" && x["status"] == "Open" }
filters the array returning an array of hashes where the selected keys have the searched values.
However, the user must be able to filter this array based on any or all of the keys having values the user submits.
While I can substitute the values from a form's params into the block like this:
results = #network.select { |x| x[params[1]["column"]] == params[1]["search"] && x[params[2]["column"]] == params[2]["search"] }
The logic of each select could be different. There could be as many as 10 different conditions with a column value and a search value in the form params.
I need a way to dynamically create the expression in the block portion of the select based on the conditions the user submits.
Unfortunately, every way I've tried to construct an expression for the block results in a string value that can not be evaluated by the select.
I've working on this for days, so I'd be very grateful if someone could give me a solution.
EDIT:
Thanks to Wand Maker's elegant solution, I made the following modifications, based on his code, to allow the user to filter the array of hashes based on keys whose search value starts with a value the user submits, instead of being equal to the value:
pm = params.map { |h| {h["column"] => h["search"].downcase} }.reduce(&:merge)
result = #network.select do |h|
temp = h.slice(*pm.keys)
new_temp = Hash.new
temp.each do |k,v|
new_temp[k]=v.downcase.slice(0..pm[k].length - 1)
end
new_temp == pm
end
This now works great.
Here is one possible way.
Let's define params to be:
params = [{"column" => "context", "search" => "r178"},
{"column" => "campaign", "search" => "Art Education"}]
We will process it to the structurally resemble the elements of #network.
pm = params.map { |h| {h["column"] => h["search"]} }.reduce(&:merge)
#=> {"context"=>"r178", "campaign"=>"Art Education"}
Now, we will pick the keys present in this processed params hash pm, and use it to get slice of each element from #network array, and if both the processed params hash and sliced hash are equal, then, we have a match and we can select the item.
result = #network.select {|h| h.slice(*pm.keys) == pm}
Complete code sample, I have added require "active_support/core_ext/hash" so that below program can be run as standalone ruby program for illustration purpose. It will not be needed in Rails code.
require "pp"
require "active_support/core_ext/hash"
#network = [{"id"=>1, "context"=>"r178", "asset"=>"Art Schools Hub", "campaign"=>"Fashion Careers", "contact_email"=>"evert_nolan#hammechaefer.net", "notes"=>"", "user_first_name"=>"Agustin", "user_last_name"=>"Welch", "status"=>"Completed", "date_collected"=>"01/22/16"},
{"id"=>4, "context"=>"r178", "asset"=>"Art Schools Hub", "campaign"=>"Graphic Design Careers", "contact_email"=>"jamil_brakus#effertz.biz", "notes"=>"", "user_first_name"=>"Agustin", "user_last_name"=>"Welch", "status"=>"In Progress", "date_collected"=>"01/22/16"},
{"id"=>15, "context"=>"r178", "asset"=>"Art Schools Hub", "campaign"=>"Art Education", "contact_email"=>"miss_kyle_mccullough#hicklezboncak.net", "notes"=>"", "user_first_name"=>"Jermaine", "user_last_name"=>"Wilkinson", "status"=>"Open", "date_collected"=>"01/22/16"}]
params = [{"column" => "context", "search" => "r178"},
{"column" => "campaign", "search" => "Art Education"}]
pm = params.map { |h| {h["column"] => h["search"]} }.reduce(&:merge)
pp result = #network.select {|h| h.slice(*pm.keys) == pm}
#=> [{"id"=>15,
# "context"=>"r178",
# "asset"=>"Art Schools Hub",
# ...
# "status"=>"Open",
# "date_collected"=>"01/22/16"}]
With respect to clarification sought in the comments, the solution can be adapted for starts_with type of condition as well. One can use:
pp result = #network.select {|h| pm.keys.all?{|k| h[k].starts_with? pm[k]}}

match key of hash and then fetch values accordingly in ruby

I have included the given code:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = params[:test] #I get "1,3,7"
I get this, now please guide me how to match keys with #amaze and accordingly fetch its values from #classes i.e USA, France, Delhi.
Since #amaze is just a String, lets first convert it in Array so its easy to enumerate:
#amaze = "1,3,7"
#amaze = #amaze.split(",")
# => ["1", "3", "7"]
Now, since you have all keys extract all values:
#amaze.map { |i| #classes[i.to_i] }
# => ["USA", "France", "Delhi"]
Split #amaze by , and get an array of keys, convert them into Integer, then select only those key/value pairs which key is into this array of keys. Something like this:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = "1,3,7" #I get "1,3,7"
arr = #amaze.split(',').map(&:to_i)
p #classes.select{|el| arr.include? el}
Result:
#> {1=>"USA", 3=>"France", 7=>"Delhi"}
If you want values only use .values:
p #classes.select{|el| arr.include? el}.values
Result:
#> ["USA", "France", "Delhi"]
For what(seemingly) you are asking, the below line will do it:
#amaze.split(",").each { |i| p #classes[i.to_i] }
# If #amaza = "1,3,7", above line will output:
# => "USA"
# "France"
# "UK"
This should work well for you:
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
#amaze = params[:test].split(",").map(&:to_i)
#classes.values_at(*#amaze)
#=> ["USA", "France", "Delhi"]
Hash#values_at accepts an indefinite number of keys and returns their values as an array. The * (splat) operator explodes the array so this call actually becomes #classes.values_at(1,3,7) Docs
Might also want to add a compact to the end in the event a key does not exist. e.g
#amaze = params[:test].split(",").map(&:to_i) # Asssume this returns [1,3,7,9]
#classes.values_at(*#amaze)
#=> ["USA", "France", "Delhi",nil]
#classes.values_at(*#amaze).compact
#=> ["USA", "France", "Delhi"]
I think a clearer understanding of hashes would help you out here.
A Hash is a data structure that is a list of key-value pairs. For example, the following is a Hash object of key-value pairs (your example):
#classes = {1=>"USA", 3=>"France", 2=>"UK", 5=>"Europe", 7=>"Delhi", 8=>"test"}
If you want to extract a value from #classes, you need to pass the key of the value you want. If we wanted "USA" we would pass the key of 1 to #classes. If we wanted "France", we would pass it the key of 3:
#classes[1] would return "USA" and #classes[3] would return "France".
It's not clear what data structure #amaze is according to your question, but let's say it's the string "1, 3, 7" which we can split to create an array [1, 3, 7].
You could iterate over the array to get each of the values from #classes:
#amaze.split(",").map(&:to_i).each do |key|
puts #classes[key]
end
That would print out each of the corresponding values to keys in #classes.

Ruby on Rails - Hash of Arrays, group by and sum by column name

I have the following array of hashes:
[{"idx"=>"1234", "account"=>"abde", "money"=>"4.00",
"order"=>"00001"}, {"idx"=>"1235", "account"=>"abde", "money"=>"2.00",
"order"=>"00001"}, {"idx"=>"1235", "account"=>"abde", "money"=>"3.00",
"order"=>"00002"}]
Like how sql does it, I'd like to take that array of hashes and group it by the order number so that it results like this where order 00001 is grouped and it sum the money to 6.00:
[{"idx"=>"1234", "account"=>"abde", "money"=>"6.00",
"order"=>"00001"}, {"idx"=>"1234", "account"=>"abde", "money"=>"3.00",
"order"=>"00002"}]
Thanks.
you can make your own method for that, something like:
def group_hashes arr, group_field, sum_field
arr.inject({}) do |res, h|
(res[h[group_field]] ||= {}).merge!(h) do |key, oldval, newval|
key.eql?(sum_field) ? (oldval.to_f + newval.to_f).to_s : oldval
end
res
end.values
end
a call group_hashes arr, "order", "money" on you array of hashes example returns:
[{"idx"=>"1234", "account"=>"abde", "money"=>"6.0", "order"=>"00001"}, {"idx"=>"1235", "account"=>"abde", "money"=>"3.00", "order"=>"00002"}]

Resources