How to sort a hash by values - ruby-on-rails

I was trying to sort a particular hash by values. I came across a way using the method sort_by. But even though I call sort_by on a hash, it returns an array, i.e.:
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
a.sort_by{|key, value| value}
# => [[2, "a"], [1, "b"], [0, "c"]]
If I try to convert the result into a hash, I end up with a hash sorted on key, hence the whole purpose of sort_by is lost. i.e.:
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
Hash[*a.sort_by{|key, value| value}.flatten]
# => {0=>"c", 1=>"b", 2=>"a"}
Is there a way I can sort a hash by value and yet get the results back in the form of a Hash?
I am using 1.8.6 ruby

You can use ActiveSupport::OrderedHash for Ruby 1.8:
ActiveSupport::OrderedHash implements a hash that preserves insertion order, as in Ruby 1.9
I don't have 1.8.6 running, but this should work:
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
ordered = ActiveSupport::OrderedHash[*a.sort_by{|k,v| v}.flatten]
ordered.keys
# => [2, 1, 0], this order is guaranteed
As noted in the quote above hashes in Ruby 1.9 "enumerate their values in the order that the corresponding keys were inserted", so this is only needed for Ruby 1.8.

A Hash is a collection of key-value pairs. It is similar to an Array,
except that indexing is done via arbitrary keys of any object type,
not an integer index. The order in which you traverse a hash by either
key or value may seem arbitrary, and will generally not be in the
insertion order.
Source: Ruby 1.8.7 docs
Hash preserves order. It enumerates its elements in the
Source: Ruby 1.9.1 changelog
If you want to use a ordered hash in Ruby 1.8, you should look at ActiveSupport's OrderedHash
You don't need to be in a Rails project, just include ActiveSupport in your project.

Your description is wrong.
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
Hash[*a.sort_by{|key, value| value}.flatten]
gives:
{
2 => "a",
1 => "b",
0 => "c"
}
which is sorted by value. By the way, your code is redundant. A better way to to this is:
Hash[a.sort_by{|_, value| value}]
or
Hash[a.sort_by(&:last)]
After Seeing Your Edit
You are using a version that does not have ordered hash. You cannot control the order of the elements. You cannot do anything with it.

Related

How to remove the outer array but still keep the inner elements in their arrays in a Rails SQL string?

I have this array:
array = [["1","one"], ["2","two"], ["3","three"]]
How do I remove the outer square brackets, removing one dimension from the array, so it looks like this:
array = ["1","one"], ["2","two"], ["3","three"]
I know if I wanted to flatten the entire array, so I get one large array, I could use flatten. However, I just want to get rid of the outer dimension, while keeping the inner elements within their respective arrays.
Essentially I am trying to get the query:
Phone.where(["brand_name in (?) AND os in (?) AND price_category in (?)", ["Nokia"], ["Blackberry", "Android"], ["1", "2"]])
But instead, this is what I am getting. Notice one more set of array brackets around the corresponding column values.
Phone.where(["brand_name in (?) AND os in (?) AND price_category in (?)", [["Nokia"], ["Blackberry", "Android"], ["1", "2"]]])
This is the method:
def self.checkbox_search(params_hash)
params_array = ["brand_name", "os", "price_category"]
column_array = []
key_array = []
params_hash.each do |k,v|
if params_array.include?(k)
column_array << v
key_array << k + ' in (?)'
end
end
joined_keys = key_array.join(" AND ") + ", " + "#{column_array}"
Phone.where([#{joined_keys}])
end
I am grabbing the params hash, and putting it in checkbox_search, which goes through the hash and puts the key values in key_array, and puts their values in column_array, if they meet specified criteria of key includes params_array. Then I join the entire string together in joined_keys, then put the results of joined_keys inside Phone.where() string
You're just assembling the arrays the wrong way:
Phone.where([ key_array.join(" AND ") ] + column_array)
That appends the column_array values. If you inline them then they'll be pushed down in terms of nesting. Note that #{...} has no place here, that's used for string interpolation and it will mess up things badly.
Technically the second version is equivalent to the first due to how it's parsed and assigned:
x = [1,2],[3,4]
# => [[1, 2], [3, 4]]
x
# => [[1, 2], [3, 4]]
That notation's normally used for situations like this:
x,y = [1,2],[3,4]
# => [[1, 2], [3, 4]]
x
# => [1, 2]
y
# => [3, 4]
There's no "outer dimension" you can remove. Either you have an array of arrays, or you have a singular array that's flat.
Reading your comment about the call to Phone you can try
array = [["1","one"], ["2","two"], ["3","three"]]
Phone.where(brand_name: array.map(&:last))
Your solution is very creative but you're greatly overcomplicating a simple task.
def self.checkbox_search(params_hash)
where(params_hash.slice(:brand_name, :os, :price_category))
end
If you only want certain keys from a hash you can use Hash#slice or for a params hash you can use ActionController::Parameters#permit.
There is absolutely no need to construct a SQL string manually. In ActiveRecord you can create WHERE ... AND ... conditions by:
Person.where(name: 'Max', awesome: true)
# or
Person.where(name: 'Max').where(awesome: true)
Passing an array as the value for a key creates a WHERE ... IN ...:
Person.where(name: ['Max', 'the12'])
See:
http://guides.rubyonrails.org/active_record_querying.html

Sort hash with nil key and array values

I have a hash like this (it is the result of the group_by method):
{nil => [#<ActiveRecord id ..., ...], #<ActiveRecord ...> => [#<ActiveRecord id ..., ...], ...}
I need to sort it so that the nil elements would be the first, and then all the other ActiveRecord objects. How can I do this?
Thanx
P.S.
Yes, I need an ActiveRecord objects as the keys (not symbols or some else)
I can't do the order in my DB due to complex SQL request.
I need only to sort the hash by the keys.
You cannot sort a hash, but:
Hashes enumerate their values in the order that the corresponding keys were inserted.
To get a specific key at the beginning, just make sure to insert it first. Here's an example:
array = [Integer, Range, BasicObject]
hash = array.group_by(&:superclass)
#=> {Numeric=>[Integer], Object=>[Range], nil=>[BasicObject]}
To get nil first, create a hash with a nil key and merge! the new values:
hash = {nil => nil}
hash.merge!(array.group_by(&:superclass))
#=> {nil=>[BasicObject], Numeric=>[Integer], Object=>[Range]}
Assuming you have your hash in h:
Hash[h.sort { |a,b|
NilClass === a.first ?
(NilClass === b.first ? 0 : -1) :
(NilClass === b.first ? 1 : a.first <=> b.first)
}]
Here we explicitly define sort function, which will place nils in front, following by native ordering of other elements by keys (ActiveRecord instances in your case.)
Sidenote: you always can do sorting in database.

Retrieving an element of an array that is a value of a hash with Ruby

I have the following hash:
hash = {"A" =>[1,2,3,4]}
Within that hash is a key "A" with the value of [1,2,3,4].
Is there a possible way to access a single element within my array using the key-value pair?
Example (...yes I know this isn't legal Ruby):
hash["A",0] => 1
Or have the ability to see if the array included a value with the key-value pair?
hash["A".include? 4] => true
Did you mean this?:
hash = {"A" =>[1,2,3,4]}
hash["A"][0] #=> 1
hash["A"].include? 4 #=> true

What does "<<" exactly do in ruby?

I am new to Ruby, so I am still learning several things. But, I do have good experience with Java and C.
I would like to know what this does exactly:
[ 'a','b', 'c' ].each_with_index {|item, index| result << [item, index] }
Specifically, I am interested in the <<. Some research tells me that it is used for bit shifting, but it's obvious that is not the case here, so what is it doing here?
The << operator is adding items to the result array in this case.
See " how to add elements to ruby array (solved)".
In Ruby, all the things which are operators in C/Java, like +, -, *, /, and so on, are actually method calls. You can redefine them as desired.
class MyInteger
def +(other)
42 # or anything you want
end
end
Array defines the << method to mean "push this item on the end of this array". For integers, it's defined to do a bit shift.
Aside from Array, many other classes define << to represent some kind of "appending" operation.
It's the Array append operator.
<< is a method, and will do different things for different classes. Array uses it to push an object onto the end of an array. Fixnums use it to shift.
This is basically an Append Operator.
It was be used with to append either an element to an array or a substring to string
For Arrays
1.9.2-p290 :009 > arr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
1.9.2-p290 :010 > arr << 6
=> [1, 2, 3, 4, 5, 6]
1.9.2-p290 :011 >
For Strings
1.9.2-p290 :011 > str = "ruby"
=> "ruby"
1.9.2-p290 :012 > str << 'rails'
=> "rubyrails"
1.9.2-p290 :013 >

My hashes are stacking unordered..What's a loop that can select them by their Hash ID?

My Hashes are appearing like this:
{"6"=>{":amount_paid"=>"100.00", ":date_paid"=>"4/22/2009"},
"0"=>{":amount_paid"=>"100.00", ":date_paid"=>"2/27/2008"},
"1"=>{":amount_paid"=>"80.00", ":date_paid"=>"3/27/2008"},
"2"=>{":amount_paid"=>"100.00", ":date_paid"=>"5/8/2008"},
"3"=>{":amount_paid"=>"100.00", ":date_paid"=>"6/20/2008"},
"4"=>{":amount_paid"=>"100.00", ":date_paid"=>"9/22/2008"},
"5"=>{":amount_paid"=>"100.00", ":date_paid"=>"2/20/2009"}}
The order matters to me when I loop through it with this:
params[:payments].each_with_index do |item, idx|
In this way I can add the dates by which ever date came before them.
Is there a loop that could find the sequence of "0".."6" and remain close to the same syntax?
The only other alternative I can think of is to ensure that those params get stacked in order. They come from a form like this :
= text_field_tag "payments[0][:date_paid]"
= text_field_tag "payments[0][:amount_paid]"
= text_field_tag "payments[1][:date_paid]"
= text_field_tag "payments[1][:amount_paid]"
= submit_tag 'punch it chewy!'
Hashes are unordered in Ruby 1.8, and ordered by insertion in Ruby 1.9. You can sort your hash by the key by using Enumerable#sort as seen in this thread. What you get out isn't a Hash but an array of arrays, with the first element as the keys and the second as the values. You will need to unpack these to get what you want similar to the each_with_index.
params[:payments].sort { |a, b| a[0].to_i <=> b[0].to_i }.each do |x|
item = x[1]
index = x[0]
.....
end
This has a similar syntax:
(0..6).each do |idx| item=params[:payments][idx]
# ...
end
Hash apparently keeps keys in the order they are inserted ( http://www.ruby-doc.org/core/classes/Hash.html ), so you can re-create a sorted hash this way:
Hash[params[:payments].sort]
(Apparently since Ruby 1.9.2; maybe not in all implementations)
Hashes are unordered. There is a gem called facets which has a dictionary object that is ordered.
You could also convert the hash to an array and then sort the array.
thing = {"1" => {:paid => 100, :date => '1/1/2011'}, "2" => {:paid => 100, :date => '1/12/2011'}}
thing.to_a.sort
thing.inspect
returns: [["1", {:date=>"1/1/1900", :paid=>100}], ["2", {:date=>"1/1/1900", :paid=>100}]]
You can then loop through the array in the correct order.
sorted_payments = params[:payments].keys.sort.map {|k| params[:payments][k]}
returns an array of hashes ordered by the value of the keys, which you can then enumerate with .each. This is more generalized than doing (0..6), which might (or might not) be useful.
(0..6).each do |idx|
item = params[:payments][idx]
end

Resources