ruby sort_by method - ruby

I have just started to learn ruby. I have an array of hashes. I want to be able to sort the array based on an elementin the hash. I think I should be able to use the sort_by method. Can somebody please help?
#array of hashes
array = []
hash1 = {:name => "john", :age => 23}
hash2 = {:name => "tom", :age => 45}
hash3 = {:name => "adam", :age => 3}
array.push(hash1, hash2, hash3)
puts(array)
Here is my sort_by code:
# sort by name
array.sort_by do |item|
item[:name]
end
puts(array)
Nothing happens to the array. There is no error either.

You have to store the result:
res = array.sort_by do |item|
item[:name]
end
puts res
Or modify the array itself:
array.sort_by! do |item| #note the exclamation mark
item[:name]
end
puts array

You can do it by normal sort method also
array.sort { |a,b| a[:name] <=> b[:name] }
Above is for ascending order, for descending one replace a with b. And to modify array itself, use sort!

You can use sort by method in one line :
array.sort_by!{|item| item[:name]}

Related

Looking to write this ruby code in one line

I want to display my hash in a string like this:
The results are a=100, b=200
When I loop through the hash like so:
a = [{:a => 100}, {:b => 200}]
a.each do |x|
x.each do |k,v|
puts "#{k}=#{v}"
end
end
the output returns strings in multiple lines
a=100
b=200
How can I change it to one string?
I was able to get it working with this
puts a.map{|x|x.map{|k,v|"#{k}=#{v}"}}.join(',')
Not sure if its the best solution though.
{:a => 100, :b => 200}.each { |k,v| puts "#{k}=#{v}" }

Recursively convert all numeric strings to integers in a Ruby hash

I have a hash of a random size, which may have values like "100", which I would like to convert to integers. I know I can do this using value.to_i if value.to_i.to_s == value, but I'm not sure how would I do that recursively in my hash, considering that a value can be either a string, or an array (of hashes or of strings), or another hash.
This is a pretty straightforward recursive implementation (though having to handle both arrays and hashes adds a little trickiness).
def fixnumify obj
if obj.respond_to? :to_i
# If we can cast it to a Fixnum, do it.
obj.to_i
elsif obj.is_a? Array
# If it's an Array, use Enumerable#map to recursively call this method
# on each item.
obj.map {|item| fixnumify item }
elsif obj.is_a? Hash
# If it's a Hash, recursively call this method on each value.
obj.merge( obj ) {|k, val| fixnumify val }
else
# If for some reason we run into something else, just return
# it unmodified; alternatively you could throw an exception.
obj
end
end
And, hey, it even works:
hsh = { :a => '1',
:b => '2',
:c => { :d => '3',
:e => [ 4, '5', { :f => '6' } ]
},
:g => 7,
:h => [],
:i => {}
}
fixnumify hsh
# => {:a=>1, :b=>2, :c=>{:d=>3, :e=>[4, 5, {:f=>6}]}, :g=>7, :h=>[], :i=>{}}
This is my helper class. It only converts Strings which are just numbers (Integer or Float).
module Helpers
class Number
class << self
def convert(object)
case object
when String
begin
numeric(object)
rescue StandardError
object
end
when Array
object.map { |i| convert i }
when Hash
object.merge(object) { |_k, v| convert v }
else
object
end
end # convert
private
def numeric(object)
Integer(object)
rescue
Float(object)
end # numeric
end # << self
end # Number
end # Helpers
Helpers::Number.convert [{a: ["1", "22sd"]}, 2, ['1.3', {b: "c"}]]
#=> [{:a=>[1, "22sd"]}, 2, [1.3, {:b=>"c"}]]

Best way to find a hash that contains a key, in an array of hashes in ruby?

Say we have
[{:name=>"Joe", :age => 15},{:name=>"Josh", :age => 83},{:name=>"Jim", :age => 1203}]
What is the best way way to get the hash where the name is "Joe"? To return {:name=>"Joe", :age=>15}?
array.select will return an array of values (even if there is only one match):
my_array.select { |item| item[:name] == "Joe" }
# => [{:name => "Joe", :age => 15}]
You can use Enumerable's find method instead to return the first instance (as a hash):
my_array.find { |item| item[:name] == "Joe" }
# => {:name => "Joe", :age => 15}
array.select { |e| e[:name] == 'Joe' }.first
If this is an operation you need to perform frequently, then store the set of hashes as a hash of hashes, with the value for the name key as the key to each hash. That way you can look up your hashes in O(1) time.

hash assignment when (key => value) are stored in an array? (ruby)

I have hash (#post) of hashes where I want to keep the order of the hash's keys in the array (#post_csv_order) and also want to keep the relationship key => value in the array.
I don't know the final number of both #post hashes and key => value elements in the array.
I don't know how to assign the hash in a loop for all elements in the array. One by one #post_csv_order[0][0] => #post_csv_order[0][1] works nicely.
# require 'rubygems'
require 'pp'
#post = {}
forum_id = 123 #only sample values.... to make this sample script work
post_title = "Test post"
#post_csv_order = [
["ForumID" , forum_id],
["Post title", post_title]
]
if #post[forum_id] == nil
#post[forum_id] = {
#post_csv_order[0][0] => #post_csv_order[0][1],
#post_csv_order[1][0] => #post_csv_order[1][1]
##post_csv_order.map {|element| element[0] => element[1]}
##post_csv_order.each_index {|index| #post_csv_order[index][0] => #post_csv_order[index][1] }
}
end
pp #post
desired hash assignment should be like that
{123=>{"Post title"=>"Test post", "ForumID"=>123}}
The best way is to use to_h:
[ [:foo,1],[:bar,2],[:baz,3] ].to_h #=> {:foo => 1, :bar => 2, :baz => 3}
Note: This was introduced in Ruby 2.1.0. For older Ruby, you can use my backports gem and require 'backports/2.1.0/array/to_h', or else use Hash[]:
array = [[:foo,1],[:bar,2],[:baz,3]]
# then
Hash[ array ] #= > {:foo => 1, :bar => 2, :baz => 3}
This is available in Ruby 1.8.7 and later. If you are still using Ruby 1.8.6 you could require "backports/1.8.7/hash/constructor", but you might as well use the to_h backport.
I am not sure I fully understand your question but I guess you want to convert a 2d array in a hash.
So suppose you have an array such as:
array = [[:foo,1],[:bar,2],[:baz,3]]
You can build an hash with:
hash = array.inject({}) {|h,e| h[e[0]] = e[1]; h}
# => {:foo=>1, :bar=>2, :baz=>3}
And you can retrieve the keys in correct order with:
keys = array.inject([]) {|a,e| a << e[0] }
=> [:foo, :bar, :baz]
Is it what you were looking for ?
Answers summary
working code #1
#post[forum_id] = #post_csv_order.inject({}) {|h,e| h[e[0]] = e[1]; h}
working code #2
#post[forum_id] = Hash[*#post_csv_order.flatten]
working code #3
#post[forum_id] ||= Hash[ #post_csv_order ] #requires 'require "backports"'

How can I assign multiple values to a hash key?

For the sake of convenience I am trying to assign multiple values to a hash key in Ruby. Here's the code so far
myhash = { :name => ["Tom" , "Dick" , "Harry"] }
Looping through the hash gives a concatenated string of the 3 values
Output:
name : TomDickHarry
Required Output:
:name => "Tom" , :name => "Dick" , :name => "Harry"
What code must I write to get the required output?
myhash.each_pair {|k,v| v.each {|n| puts "#{k} => #{n}"}}
#name => Tom
#name => Dick
#name => Harry
The output format is not exactly what you need, but I think you get the idea.
The answers from Rohith and pierr are fine in this case. However, if this is something you're going to make extensive use of it's worth knowing that the data structure which behaves like a Hash but allows multiple values for a key is usually referred to as a multimap. There are a couple of implementations of this for Ruby including this one.
You've created a hash with the symbol name as the key and an array with three elements as the value, so you'll need to iterate through myhash[:name] to get the individual array elements.
re: the issue of iterating over selective keys. Try using reject with the condition inverted instead of using select.
e.g. given:
{:name=>["Tom", "Dick", "Harry"], :keep=>[4, 5, 6], :discard=>[1, 2, 3]}
where we want :name and :keep but not :discard
with select:
myhash.select { |k, v| [:name, :keep].include?(k) }
=> [[:name, ["Tom", "Dick", "Harry"]], [:keep, [4, 5, 6]]]
The result is a list of pairs.
but with reject:
myhash.reject { |k, v| ![:name, :keep].include?(k) }
=> {:name=>["Tom", "Dick", "Harry"], :keep=>[4, 5, 6]}
The result is a Hash with only the entries you want.
This can then be combined with pierr's answer:
hash_to_use = myhash.reject { |k, v| ![:name, :keep].include?(k) }
hash_to_use.each_pair {|k,v| v.each {|n| puts "#{k} => #{n}"}}

Resources