Sort hash with nil key and array values - ruby-on-rails

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.

Related

Pluck doesn't return a bother records they have the same name

I have a pluck that is turned into a hash and stored in a variable
#keys_values_hash = Hash[CategoryItemValue.where(category_item_id: #category_item.id).pluck(:key, :value)]
If 2 records have the same :key name then only the most recent record is used and they aren't both added to the hash. But if they have the same value and different keys both are added to the hash.
This also occurs if I swap :key and :value around (.pluck(:value, :key)). If they have now the same value it only uses the most recent one and stores that in the hash. But having the same key is now fine.
I'm not sure of this is being caused by pluck or from being sorted in a hash. I'm leaning towards pluck being the culprit.
What is causing this and how can I stop it from happening. I don't want data being skipped if it has the same key name as another record.
I'm not sure why you need convert pluck result into a Hash, because it was an Array original.
Like you have three CategoryItemValue like below:
id, key, value
1, foo, bar1
2, foo, bar2
3, baz, bar3
when you pluck them directly, you will get a array like:
[ ['foo', 'bar1'], ['foo', 'bar2'], ['baz', 'bar3'] ]
but when you convert it into a hash, you will get:
{'foo' => 'bar2', 'baz' => 'bar3' }
because new hash value will override the old one if key ( foo in the example above) exists.
Or you could try Enumerable#group_by method:
CategoryItemValue.where(...).pluck(:key, :value).group_by { |arr| arr[0] }

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

find_all elements in an array that match a condition?

I've an array of hash entries, and want to filter based on a paramater passed into the function.
If there are three values in the hash, A, B, and C, I want to do something similar to:
data = [{A:'a1', B:'b1', C:'c1'},
{A:'a1', B:'b2', C:'c1'},
{A:'a1', B:'b2', C:'c2'},
{A:'a2', B:'b1', C:'c1'},
{A:'a2', B:'b2', C:'c1'}]
data.find_all{ |d| d[:A].include?params[:A] }
.find_all{ |d| d[:B].include?params[:B] }
.find_all{ |d| d[:C].include?params[:C] }
find all where A == 'a1' AND B='b2'
so for above I get:
{A:'a1', B:'b2', C:'c1'} and {A:'a1', B:'b2', C:'c2'}
* put if / else statements, like params.has_key? 'B' then do something.
* modify my code each time a new type of value is added to the Hash map (say now I have 'D').
Note: The key of the hash is a symbol, but the value is a string and I want to do a "contains" not "equals".
I think of it as SQL Select statement with where zero or more '==' clause
If I understand your question correctly, then I believe that the select method of Array class may be helpful to you.
select takes a block of code which is intended to be a test condition on each element in your array. Any elements within your array which satisfy that test condition will be selected, and the result is an array of those selected elements.
For example:
arr = [ 4, 8, 15, 16, 23, 42 ]
result = arr.select { |element| element > 20 }
puts result # prints out [23, 42]
In your example, you have an array of hashes, which makes it only slightly more complicated than my example with a simple array of numbers. In your example, we have:
data = [{A:'a1', B:'b1', C:'c1'},
{A:'a1', B:'b2', C:'c1'},
{A:'a1', B:'b2', C:'c2'},
{A:'a2', B:'b1', C:'c1'},
{A:'a2', B:'b2', C:'c1'}]
I believe what you want your code to do is something like: Select from my data array all of the hashes where, within the hash, :A equals some value AND :B equals some other value.
Let's say you want to find all of the hashes where :A == 'a1' and :B == 'b2'. You would do that like this:
data.select { |hash_element| hash_element[:A] == 'a1' && hash_element[:B] == 'b2' }
This line returns to you an array with those hashes from your original data array which satisfy the condition we provided in the block - that is, those hashes where :A == 'a1' and :B == 'b2'. Hope that that helps shed some light on your problem!
More information about the select method here:
http://www.ruby-doc.org/core-2.1.0/Array.html#method-i-select
edited - below is an addition to original answer
To follow up on your later question about if/else clauses and the addition of new parameters... the block of code that you pass to select can, of course, be much more complicated than what I've written in the example. You just need to keep this in mind: If the last line of the block of code evaluates to true, then the element will be selected into the result array. Otherwise, it won't be selected.
So, that means you could define a function of your own, and call that function within the condition block passed to select. For example, something like this:
def condition_test(hash_element, key_values)
result = true
key_values.each do |pair|
if hash_element[pair[:key]] != pair[:value]
result = false
end
end
return result
end
# An example of the key-value pairs you might require to satisfy your select condition.
requirements = [ {:key => :A, :value => 'a1'},
{:key => :B, :value => 'b2'} ]
data.select { |hash_element| condition_test(hash_element, requirements) }
This makes your condition block a bit more dynamic, rather than hard-wired for :A == 'a1' and :B == 'b2' like we did in the earlier example. You can tweak requirements on the fly based on the keys and values that you need to look for. You could also modify condition_test to do more than just check to see if the hash value at some key equals some value. You can add in your if/else clauses in condition_test to test for things like the presence of some key, etc.
I think you want to use .values_at(key)
http://www.ruby-doc.org/core-2.1.0/Hash.html#method-i-values_atvalues_at method

How to check if specific value is present in a hash?

I'm using Rails and I have a hash object. I want to search the hash for a specific value. I don't know the keys associated with that value.
How do I check if a specific value is present in a hash? Also, how do I find the key associated with that specific value?
Hash includes Enumerable, so you can use the many methods on that module to traverse the hash. It also has this handy method:
hash.has_value?(value_you_seek)
To find the key associated with that value:
hash.key(value_you_seek)
This API documentation for Ruby (1.9.2) should be helpful.
The simplest way to check multiple values are present in a hash is:
h = { a: :b, c: :d }
h.values_at(:a, :c).all? #=> true
h.values_at(:a, :x).all? #=> false
In case you need to check also on blank values in Rails with ActiveSupport:
h.values_at(:a, :c).all?(&:present?)
or
h.values_at(:a, :c).none?(&:blank?)
The same in Ruby without ActiveSupport could be done by passing a block:
h.values_at(:a, :c).all? { |i| i && !i.empty? }
Hash.has_value? and Hash.key.
Imagine you have the following Array of hashes
available_sports = [{name:'baseball', label:'MLB Baseball'},{name:'tackle_football', label:'NFL Football'}]
Doing something like this will do the trick
available_sports.any? {|h| h['name'] == 'basketball'}
=> false
available_sports.any? {|h| h['name'] == 'tackle_football'}
=> true
While Hash#has_key? works but, as Matz wrote here, it has been deprecated in favour of Hash#key?.
Hash's key? method tells you whether a given key is present or not.
hash.key?(:some_key)
The class Hash has the select method which will return a new hash of entries for which the block is true;
h = { "a" => 100, "b" => 200, "c" => 300 }
h.select {|k,v| v == 200} #=> {"b" => 200}
This way you'll search by value, and get your key!
If you do hash.values, you now have an array.
On arrays you can utilize the Enumerable search method include?
hash.values.include?(value_you_seek)
An even shorter version that you could use would be hash.values

Rails group by id uses a string for hash key

My code looks like this:
hash = MyModel.count(:group => 'id', :conditions => 'bla = "bla"')
The returned Hash has keys that are strings. I want them to be ints. I know it would be possible to convert the Hash manually using something like a map construct.
Edit:
Thanks for the responses. Have realised it was a json conversion process that was turning the ids into Strings and rails does in fact use the Fixnum as one might expect.
hash = MyModel.count(group: 'id', conditions: 'bla = "bla"')
should have Fixnum keys by default since id is an instance of Fixnum.
What happens is that ActiveRecord always fetch result as strings and then Rails takes care of converting them to other datatypes according to the type of the database column (we say that they are typecast).
So it's maybe a Rails bug or the 'id' column is not set as integer(which would be surprising).
If you can't fix it, convert them manually:
hash.each_with_object({}) do |(key, value), hash|
hash[key.to_i] = value
end
When I use your code I get integer keys (rails 3.07), what's the column type of id?
If you want to do it manually:
new_hash = hash.inject({}){|h,a| h[a.first.to_i] = a.last; h}
new_hash = Hash[hash.map { |k, v| [k.to_i, v] }

Resources