I used group_by to get a certain desired result. Based on the explanation in the answer, I have updated my question to reflect the answer, to see the steps it took to arrive at a solution, see the edit history.
#grouped_test_specific_reports = TestSpecificReport.all.group_by(&:equipment_type_name)
The code above produced this result:
2.5.1 :026 > pp #grouped_test_specific_reports
{"Ultrasonic Probes"=>
[#<TestSpecificReport:0x00007f832aa2d6e0
id: 10,
equipment_type_id: 2,
test_method_id: 1,
equipment_amount: "Multiple",
equipment_heading: "UT Probes">],
"Ultrasonic Instruments"=>
[#<TestSpecificReport:0x00007f832aa2d3c0
id: 8,
equipment_type_id: 1,
test_method_id: 1,
equipment_amount: "Single",
equipment_heading: "UT Instrument">],
"Visual Test Equipment"=>
[#<TestSpecificReport:0x00007f832aa2cfb0
id: 11,
equipment_type_id: 4,
test_method_id: 1,
equipment_amount: "Single",
equipment_heading: "VT Equipment">]}
=> {"Ultrasonic Probes"=>[#<TestSpecificReport id: 10, equipment_type_id: 2, test_method_id: 1, equipment_amount: "Multiple", equipment_heading: "UT Probes">], "Ultrasonic Instruments"=>[#<TestSpecificReport id: 8, equipment_type_id: 1, test_method_id: 1, equipment_amount: "Single", equipment_heading: "UT Instrument">], "Visual Test Equipment"=>[#<TestSpecificReport id: 11, equipment_type_id: 4, test_method_id: 1, equipment_amount: "Single", equipment_heading: "VT Equipment">]}
My next goal is to list out the grouped test specific report in the browser by their keys, I was able to do that by #grouped_test_specific_reports.each { |key, value| puts key }
"Visual Test Equipment"
"Ultrasonic Instruments" and
"Ultrasonic Probes"
Now we have to iterate over the values, which happens to be an array, in another loop to be able to compare equipment_amount.
The values with equipment_amount: "Multiple" will have the plus icon in front of them, and the ones with equipment_amount: "Single" will simply be a drop-down:
Here's the code for the UI:
- #grouped_test_specific_reports.each do |equipment_type_name, test_specific_reports|
.form-group.row
.col-sm-6
%label
= equipment_type_name
= select_tag '', options_from_collection_for_select(test_specific_reports, :id, :equipment_heading), { include_blank: "Select #{equipment_type_name} List", class: 'form-control select2', style: 'width: 100%;' }
.col-sm-1
- test_specific_reports.each do |test_specific_report|
- if test_specific_report.equipment_amount == 'Multiple'
.icon.text-center
%i.fa.fa-plus-circle.add-icon
I personally found the question you're asking a bit unclear. For this reason I discussed some things in the comments with you. From our discussion in the comments it seemed you simply wanted to loop through the grouped values for each group.
First I want to clear up what group_by exactly does, because this seemed to be the issue. A simple misunderstanding of what you're currently working on.
group_by { |obj| block } → a_hash
group_by → an_enumerator
Groups the collection by result of the block. Returns a hash where the keys are the evaluated result from the block and the values are arrays of elements in the collection that correspond to the key.
If no block is given an enumerator is returned.
(1..6).group_by { |i| i%3 } #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
The documentation makes clear that the grouped hash has keys that evaluate from the block (the return value). The value that belongs to the key is actually an list of values that evaluate to the same result. This means you can simply loop through the values in the following way.
grouped_values = (1..6).group_by { |n| n % 3 }
grouped_values.each do |key, values|
puts "Key: #{key}"
values.each do |value|
puts "Value: #{value}"
end
end
The first each loops through the groups. The second each loops through the values of the group. Since you loop though two different things you can't change this into a single loop easily. The important thing to remember here that the value belonging to a group key is not a single value, but rather a group of values (array).
Related
I am using a hash constant in my ROR application. I want to show the names from the hash constant to drop down.
helper.rb
PRODUCE_GROWING_METHODS = [
{id: 1, name: 'Conventional'},
{id: 2, name: 'Organic'},
]
def produce_growing_methods
PRODUCE_GROWING_METHODS
end
_produce.haml
= f.simple_fields_for :produce_details do |pd|
= pd.input :produce_growing_method, :collection => produce_growing_methods.collect { |x| [x[0], x[1]] }, :prompt => "Select Growing Method"
I tried as shown above in _produce.haml but i am getting the empty drop down. Names from the constant are not populated in drop down.
Can any one help me how to show the names from the PRODUCE_GROWING_METHODS hash constant to a drop down.
Thanks
You should map the hash by keys. In your case the keys are :id and :name:
produce_growing_methods.map { |x| [x[:id], x[:name]] }
In reality you are always better of using a generic solution rather then manual mapping.
Here is a better way of achieving the same, but it will work as well for array of thousand hashes:
ary = [
{id: 1, name: 'Conventional'},
{id: 2, name: 'Organic'},
]
ary.map(&:values)
#=> [[1, "Conventional"], [2, "Organic"]]
In Ruby on Rails 4, I'm trying to make an API for my website and instead of using an array like so:
[{id: 1, name: "John"}, {id: 2, name: "Foo"}, {id: 3, name: "Bar"}]
I want to render it like this, because it makes it much easier to search through in javascript (and for other reasons):
{"1": {id: 1, name: "John"}, "2": {id: 2, name: "Foo"}, "3": {id: 3, name: "Bar"}}
This works:
# users/index.rabl
#users.each do |user|
node(users.id.to_s) do
partial('api/v1/users/show', object: user)
end
end
But in the partial, I want another collection of elements (which belong to the user) and I can't get that working. Is there a more elegant way to do this?
To choose hash-map rather than array is definitely better option if have control on API backend codebase. From BigO notation hash lookup happens with constant O(1) time, not O(n) as for array.
Primary key lookup from the database is the most performant way to query data. Primary key is usually short and always indexed. In case of big data set use pagination.
Let's assume there is no RABL (you can always instantiate pure Ruby classes in RABL DSL code) but just an array:
array = [{id: 1, name: "John"}, {id: 2, name: "Foo"}, {id: 3, name: "Bar"}]
hash = {}
array.each{ |elem,i| hash[elem[:id].to_s] = elem }
# {"1"=>{:id=>1, :name=>"John"}, "2"=>{:id=>2, :name=>"Foo"}, "3"=>{:id=>3, :name=>"Bar"}}
To pass the Ruby hash to Javascript on client you probably want to encode it appropriately:
# hash.to_json
# {"1":{"id":1,"name":"John"},"2":{"id":2,"name":"Foo"},"3":{"id":3,"name":"Bar"}}
From Javascript you query hash by its key:
hash = {"1":{"id":1,"name":"John"},"2":{"id":2,"name":"Foo"},"3":{"id":3,"name":"Bar"}}
hash[1]
# Object { id=1, name="John"}
I'm trying to build an advanced search option (similar to Twitter's). Users can enter the words included, words excluded, words containing, exact phrase, and such in a search query.
I'm using Searchkick to do this. Particularity Searckick's regexp searches.
Here is what I'm doing to find the companies which have the words "Facebook or less" in their slogans.
Company.search("Be", where: { short_desc: /.*(#{ar}).*/ })
This works well. But, how would I do a negative of this search?
Doing something like, Company.search("Be", where: { short_desc: /.*(?!(#{ar})).*/ }) is not yielding results. Also, can I combine a search that has words to be included AND words to be excluded?
May be this helps you, try this:
Business.search("Be", where: { short_desc: {not: /.*(#{ar}).*/} })
And check the results. You can combine this with AND or OR operators. Anywhere using the where clause. Extracted from the gem readme file:
where: {
expires_at: {gt: Time.now}, # lt, gte, lte also available
orders_count: 1..10, # equivalent to {gte: 1, lte: 10}
aisle_id: [25, 30], # in
store_id: {not: 2}, # not
aisle_id: {not: [25, 30]}, # not in
user_ids: {all: [1, 3]}, # all elements in array
category: /frozen .+/, # regexp
or: [
[{in_stock: true}, {backordered: true}]
]
}
This is how you can combine it with query.
Hope this helps.
I have a few very basic questions in mind regarding active records. Please help me undestand:
What is the (data)type of the query result?
On doing '.class' to it, I get
the class name (Model name) to which it belongs. Makes complete
sense, but can we add more to the result?
Explaining my question with example:
u = User.find 1
=>#<User id: 1, email: "shivam#example.com", lang: 0, currency: 0, state: 0, category: 0, verified: true>
Checking the class:
u.class
=>User(id: integer, email: string, lang: integer, currency: integer, state: integer, category: integer, verified: boolean)
Is there someway to do this?
u.new_attr = "new_val"
Not entirely sure if this is what you want to do, but you can just select extra fields and they will be available on the object.
An example to demonstrate this:
results = Sample.find_by_sql(<<-SQL
select s.id, count(*) as results_count
from samples s, sample_results ss
where ss.sample_id = s.id
group by s.id
SQL)
So this query will return the sample-id and the count of linked sample-results. Now I can just type
results.first.results_count
and, obviously, results_count is not defined as attribute on the model Sample.
Not sure if that is what you ment. Maybe you just want to add an instance variable which is not saved to the database. In that case
you can simply do
class Sample
attr_accessor :my_special_field
end
and now I can just write
sample.my_special_field = 'something'
and it will only be available as long as the object "exists" and is never saved to the database.
Sorry if my question was not self explanatory. Here's a short explanation:
I do an active record query.
I get results.
I want to add something further to it.
As in the question the example is:
u = User.find 1
=>#<User id: 1, email: "shivam#example.com", lang: 0, currency: 0, state: 0, category: 0, verified: true>
AIM
To add some more information to the result set.
ISSUE
query result is an object of class User. As objects are instances of a class, we cannot simply add new attributes to the object without modifying the class. (Which is certainly not what we are looking for).
SOLUTION
It is plain stupidity to even think of directly modifying the object (which I did and got a -1 on my question. I acknowledge my mistake).
What we should rather do is, somehow convert the object into a data-structure that can be modified. In a case like this Hash suits the best.
Rails just provide an api to do so, its called attributes. Here:
u = User.find 1
=>#<User id: 1, email: "shivam#example.com", lang: 0, currency: 0, state: 0, category: 0, verified: true>
u.attributes
=> {"id" => 1, "email" => "shivam#example.com", "lang" => 0, "currency" => 0, "state" => 0, "category" => 0, "verified" => true}
Now that we have a hash. we can easily add/remove/modify/append any thing we want to:
u.attributes.merge({"something" => "added"})
=> {"id" => 1, "email" => "shivam#example.com", "lang" => 0, "currency" => 0, "state" => 0, "category" => 0, "verified" => true, "something" => "added"}
PS: Silly as it may, I have seen a lot of people asking such question over SO and other communities. I hope this may help someone. :)
(rdb:60) p resultsHash
{ 1 => [#<Participant id: 6, username: "player2", online_rank: 7, created_at: "2011-05-14 04:49:22", updated_at: "2011-05-14 04:57:56", win_count: 1, device_type: "iPad">],
0 => [#<Participant id: 5, username: "player1", online_rank: 3, created_at: "2011-05-12 02:47:50", updated_at: "2011-05-12 02:47:50", win_count: 0, device_type: "iPad">,
#<Participant id: 4, username: "iPhone4Simulator", online_rank: 4, created_at: "2011-05-12 02:45:37", updated_at: "2011-05-12 02:45:37", win_count: 0, device_type: "iPad">]}
I've tried...
resultsHash.sort {|a,b| -(a[0]<=>b[0])}
but the results aren't sorted by the keys when I iterate through the hash using each_pair.
Thanks!
What you really want to do is add an ORDER BY clause to your query. If this is Rails 3, Participant.order(:id).all is one way to do it.
To answer your immediate question, though, you would say resultsHash.sort_by(&:id). But don't do it this way.
Side note, use snake_case for Ruby code, not camelCase.
Edit: See comments.
resultsHash.sort.reverse
Note that it'll return an Array of [key, value] pairs. But you can still iterate like with a Hash:
resultsHash.sort.reverse.each do |key, value|
....
end
Or you can retrieve only the values: resultsHash.sort.reverse.map { |key, value| value }, or resultsHash.sort.reverse.map(&:last)
I realize this is an old question, but one way you can do exactly what you want while still retaining a return type of Hash is to use .slice on the sorted keys:
resultHash.slice(*resultHash.keys.sort)