Ruby Array sort_by value in variable - ruby

I would like to sort Ruby Array by key in variable, but I don't know how.
Situation
my_arr.sort_by {|record| [record.year]}
Will sort by a year of the record. But I want to sort by author, label, etc. And this sort type is stored in a variable like a String. So I need to evaluate the filter like
my_arr.sort_by {|record| [record."something_in_the_var"]}
Of course, I have fixed filters. But still figuring out how to do it properly.
Thanks for tips

This is what send does
str = "label"
arr.sort_by{|rec| rec.send(str) }
send is defined on BasicObject, so every object has it.

you can use something like
sort_by_this = "label"
my_arr.sort_by {|record| [record[sort_by_this]]}

Related

How can i sort an arrays of hashes in ruby

this is the code i tried to sort it by name,email,country,comments.first i tried sorting by names
array_of_hashes=[
{"Name"=>"Akash","Email"=>"akash85#gmail.com","Country"=>"India",'Comments'=>"9898984523"},
{"Email"=>"rahul#hotmail.com","Country"=>"Srilanka","Name"=>"Rahul"},
{"Country"=>"India", "Comments"=>"3455358782","Email"=>"veera#gmail.com","Name"=>"Veera"},
{"Name"=>"Akash","Country"=>"India", "Email"=>"akash37#yahoo.com", "Comments"=>"8898788932"}
]
puts array_of_hashes.sort_by { |element| element.keys(&:Name)}
but the displayed output is not as i expected,it prints the same which i mentioned above.
i expected to code the final output should be like this
Name Email Country Comments
Akash akash37#live.com India 8898788932
Akash akash85#gmail.com India 9898984523
Rahul rahul#hotmail.com Srilanka
Veera veera#gmail.com India 3455358782
Help me to resolve these.Thanks in advance!
You should look at what element.keys(&:Name) evaluates to in the block that you're passing to #sort_by. All methods in Ruby can be given a block, you can pass it even if the method doesn't use it. Hash#keys doesn't use the block so element.keys(&:Name) is the same as element.keys and you end up trying to sort by the array ['Name', 'Email', 'Country', 'Comments'].
If you want to sort by the name, say so:
hash.sort_by { |element| element['Name'] }
Keep in mind that your keys are strings so you want element['Name'] rather than element[:Name]. I'd also recommend that you don't call your array of hashes hash, that's a little confusing.
not_a_hash = [
{"Name"=>"Akash","Email"=>"akash85#gmail.com","Country"=>"India",'Comments'=>"9898984523"},
{"Email"=>"rahul#hotmail.com","Country"=>"Srilanka","Name"=>"Rahul"},
{"Country"=>"India", "Comments"=>"3455358782","Email"=>"veera#gmail.com","Name"=>"Veera"},
{"Name"=>"Akash","Country"=>"India", "Email"=>"akash37#yahoo.com", "Comments"=>"8898788932"}
]
sorted_values = not_a_hash.map{|h| h.values_at("Name", "Email", "Country", "Comments")}.sort

Create an object if one is not found

How do I create an object if one is not found? This is the query I was running:
#event_object = #event_entry.event_objects.find_all_by_plantype('dog')
and I was trying this:
#event_object = EventObject.new unless #event_entry.event_objects.find_all_by_plantype('dog')
but that does not seem to work. I know I'm missing something very simple like normal :( Thanks for any help!!! :)
find_all style methods return an array of matching records. That is an empty array if no matching records are found. And an empty is truthy. Which means:
arr = []
if arr
puts 'arr is considered turthy!' # this line will execute
end
Also, the dynamic finder methods (like find_by_whatever) are officially depreacted So you shouldn't be using them.
You probably want something more like:
#event_object = #event_entry.event_objects.where(plantype: 'dog').first || EventObject.new
But you can also configure the event object better, since you obviously want it to belong to #event_entry.
#event_object = #event_entry.event_objects.where(plantype: 'dog').first
#event_object ||= #event_entry.event_objects.build(plantype: dog)
In this last example, we try to find an existing object by getting an array of matching records and asking for the first item. If there are no items, #event_object will be nil.
Then we use the ||= operator that says "assign the value on the right if this is currently set to a falsy value". And nil is falsy. So if it's nil we can build the object form the association it should belong to. And we can preset it's attributes while we are at it.
Why not use built in query methods like find_or_create_by or find_or_initialize_by
#event_object = #event_entry.event_objects.find_or_create_by(plantype:'dog')
This will find an #event_entry.event_object with plantype = 'dog' if one does not exist it will then create one instead.
find_or_initialize_by is probably more what you want as it will leave #event_object in an unsaved state with just the association and plantype set
#event_object = #event_entry.event_objects.find_or_initialize_by(plantype:'dog')
This assumes you are looking for a single event_object as it will return the first one it finds with plantype = 'dog'. If more than 1 event_object can have the plantype ='dog' within the #event_entry scope then this might not be the best solution but it seems to fit with your description.

How do I extract a value from this Ruby hash?

I'm using the Foursquare API, and I want to extract the "id" value from this hash
[{"id"=>"4fe89779e4b09fd3748d3c5a", "name"=>"Hitcrowd", "contact"=>{"phone"=>"8662012805", "formattedPhone"=>"(866) 201-2805", "twitter"=>"hitcrowd"}, "location"=>{"address"=>"1275 Glenlivet Drive", "crossStreet"=>"Route 100", "lat"=>40.59089895083072, "lng"=>-75.6291255071468, "postalCode"=>"18106", "city"=>"Allentown", "state"=>"Pa", "country"=>"United States", "cc"=>"US"}, "categories"=>[{"id"=>"4bf58dd8d48988d125941735", "name"=>"Tech Startup", "pluralName"=>"Tech Startups", "shortName"=>"Tech Startup", "icon"=>"https://foursquare.com/img/categories/shops/technology.png", "parents"=>["Professional & Other Places", "Offices"], "primary"=>true}], "verified"=>true, "stats"=>{"checkinsCount"=>86, "usersCount"=>4, "tipCount"=>0}, "url"=>"http://www.hitcrowd.com", "likes"=>{"count"=>0, "groups"=>[]}, "beenHere"=>{"count"=>0}, "storeId"=>""}]
When I try to extract it by using ['id'], I get this error can't convert Symbol into Integer. How do I extract the value using ruby? Also, how do I do this for multiple hashes extracting the "id" value each time?
Please pardon my inexperience. Thanks!
It's wrapped in an array, that's what the [ and ] mean on the start and end. But it also looks like this array only one object in it, which is the hash you really want.
So assuming you want the first object in this array:
mydata[0]['id'] # or mydata.first['id'] as Factor Mystic suggests
But usually when an API returns an Array there is a reason (it might return many results instead of just one), and naively plucking the first item from it my not be what you want. So be sure you are getting the kind of data you really expect before hard coding this into your application.
For multiple hashes, if you want to do something with the id (run a procedure of some kind) then
resultsArray.each do |person|
id = person["id"] #then do something with the id
end
If you want to just get an array containing the ids then
resultsArray.map{|person| person["id"]}
# ["4fe89779e4b09fd3748d3c5a", "5df890079e4b09fd3748d3c5a"]
To just grab the one item from the array, see Alex Wayne's answer
To get an array of ids, try: resultsArray.map { |result| result["id"] }

How do I combine map with to_s?

I am using Mongoid and retrieving a bunch of BSON::ObjectId instances. Ideally, I'd like to convert them to strings upon retrieval. What's the correct syntax? It can be done in two lines like this:
foo = Bar.where(:some_id => N).map(&:another_id)
ids_as_strings = foo.map(&:to_s)
What's the proper Ruby way to chain to_s after the map invocation above?
This works fine, but don't do it!
ids_as_string = Bar.where(:some_id => N).map(&:another_id).map(&:to_s)
It looks cool for sure, but think about it, you are doing two maps. A map is for looping over an array, or something else, and will operate in each position, retrieving a new array, or something else, with the results.
So why do two loops if you want to do two operations?
ids_as_string = Bar.where(:some_id => N).map {|v| v.another_id.to_s}
This should be the way to go in this situation, and actually looks nicer.
You can just chain it directly:
ids_as_string = Bar.where(:some_id => N).map(&:another_id).map(&:to_s)
I tried this out with a model and I got what you expected, something like:
["1", "2", ...]

What is the proper way to define an array of values for the IN operator in Active Record

I need to search my data for rows who's value is contained in an array of strings. What is the proper way to do this in Active Record?
For example, say I have this array:
["fluffy", "spot"]
I want the SQL to look something like this:
select * FROM Pets WHERE name IN ('fluffy', 'spot')
The following works:
list = ["fluffy", "spot"].map { |x| "'#{x}'" }.join(', ')
Pet.where("name in (#{list})")
Obviously this is a bad idea. What is the correct method to do this? Is there a way to use parameters in this way, or does Active Record have a special method for this?
list = ["fluffy", "spot"]
Pet.where(:name => list)

Resources