Sorting values from a Ruby hash, ignoring case - ruby

This is how I'm handling values in a Ruby hash to get an alpha-numeric lower-case sorted output (extreme example):
myhash = {
"x" => "zebra",
"one" => "1",
"alpeh" => "alpha",
"lower" => "january",
"1" => "January",
"2" => "February",
"answer" => "42"
}
m = myhash.values
puts m.map{|i| i.downcase}.sort
Output:
1
42
alpha
february
january
january
zebra
This works fine and I don't have a problem with it, but want to know if there's there a simpler/more efficient way I'm missing?

Since you want to modify the values (outputting lowercase strings), I don't think you can do anything better.
If outputting the original values is OK as long as they are well sorted, you could use this :
myhash.values.sort_by{|h|h.downcase}
Edit : Thanks to Casper, here's a more compact version :
myhash.values.sort_by(&:downcase)
Edit : Thanks to Mischa, if you want to keep the output you provided :
myhash.values.map(&:downcase).sort

More efficient way than using hash.values (since it creates a temperary array and may be time/space consuming if the hash is large)
myhash.sort_by{|_,v| v.downcase}

This might be more efficient...
m.sort {|a, b| a.casecmp(b)}
=> ["1", "42", "alpha", "February", "January", "january", "zebra"]
Just use a test function that ignores the case on the inputs. It depends on whether you want you output array to have duplicate values (e.g. january/january vs January/january).

Related

Creating nested hash - Ruby

I have two unique hashes and I want to write a code to create a single nested hash from those two. I understand one can create a nested hash manually, but I'd rather be able to write code to do that.
cats = {"name" => "alpha", "weight" => "10 pounds"}
dogs = ("name" => "beta", "weight"=>"20 pounds"}
Ideally my nested hash would resemble:
pets = {"dogs"=>{"name"=>"beta", "weight"=>"20 pounds"}, "cats"=>{"name"=>"alpha", "weight"=>"10
pounds"}}
I'd really appreciate it if someone could break down the code to do the above for me.
Thank you!!
You can do it easily like this.
pets = {"dogs"=>dogs, "cats"=>cats}
Output:
{"dogs"=>{"name"=>"beta", "weight"=>"20 pounds"}, "cats"=>{"name"=>"alpha", "weight"=>"10 pounds"}}
Just for fun, you can also get all the local variables automatically and save them into a hash:
cats = {"name" => "alpha", "weight" => "10 pounds"}
dogs = {"name" => "beta", "weight"=>"20 pounds"}
puts binding.local_variables.map{|var| [var.to_s, binding.local_variable_get(var)] }.to_h
# {"cats"=>{"name"=>"alpha", "weight"=>"10 pounds"}, "dogs"=>{"name"=>"beta", "weight"=>"20 pounds"}}
But please the other answer instead.

How to sort an array of hashes with multiple values?

I have an array of hashes which contains an id field and a weight field. I am able to sort it based on weight but I also need to make sure that the id field is sorted if there are duplicate weights. Below is the code snippet for reference.
# Input
arr = [{"id" => 10, "weight" => 23}, {"id" => 6, "weight" => 43}, {"id" => 12, "weight" => 5}, {"id" => 15, "weight" => 30}, {"id" => 11, "weight" => 5}]
arr.sort_by{|k| k["weight"]}
# Output: [{"id"=>12, "weight"=>5}, {"id"=>11, "weight"=>5}, {"id"=>10, "weight"=>23}, {"id"=>15, "weight"=>30}, {"id"=>6, "weight"=>43}]
# Expected output = [{"id"=>11, "weight"=>5}, {"id"=>12, "weight"=>5}, {"id"=>10, "weight"=>23}, {"id"=>15, "weight"=>30}, {"id"=>6, "weight"=>43}]
In the above example, id = 12 and id = 11 have the duplicate values. I need to have id = 11 before id = 12 in the array. I really appreciate some guidance on this. Thank you!
You can use Enumerable#sort_by with an array too. Note the order of the values in the array.
arr.sort_by {|k| k.values_at("weight", "id") }
Quote from the docs of Array#<=> about how comparison of array works:
Arrays are compared in an “element-wise” manner; the first element of ary is compared with the first one of other_ary using the <=> operator, then each of the second elements, etc… As soon as the result of any such comparison is non zero (i.e. the two corresponding elements are not equal), that result is returned for the whole array comparison.
You can use Array#sort with the spaceship operator like so:
arr.sort do |a,b|
if a["weight"] == b["weight"]
a["id"] <=> b["id"]
else
a["weight"] <=> b["weight"]
end
end

Creating a map from an array of arrays in Ruby

I'm trying to write a function in Ruby which can take in a in an array of arrays, and convert it into a hash. This will be used to simulate sentences and create arbitrary word sequences. The resulting map will be helpful in generating all combination of sentences available with the current sentence rules.
How do I go about achieving this? I'm a bit lost as to where to start.
Try this:
arr = [ ["<start>", "The <object> <verb> tonight."],
["<object>", "waves", "big yellow flowers", "slugs"],
["<verb>", "sigh <adverb>", "portend like <object>", "die <adverb>"],
["<adverb>", "warily", "grumpily"] ]
arr.map { |ar| [ar.shift, ar.map { |str| str.split }] }.to_h
#=>
#{ "<start>" => [["The", "<object>", "<verb>", "tonight."]],
# "<object>" => [["waves"], ["big", "yellow", "flowers"], ["slugs"]],
# "<verb>" => [["sigh", "<adverb>"], ["portend", "like", "<object>"], ["die", "<adverb>"]],
# "<adverb>" => [["warily"], ["grumpily"]] }
ar.shift takes the first element of each subarray. The block used with ar.map splits (on whitespace) the remaining elements into arrays. Finally to_h converts the resulting array into a hash.

Get values from hash in ruby

I have hash that looks like this:
{"0"=>{"car_addition_id"=>"9"}, "1"=>{"car_addition_id"=>"10"}}
I want to get from this hash only this "10" and "9" values in array. How can I do that?
Thanks in advance!
try this out:
h = {"0"=>{"car_addition_id"=>"9"}, "1"=>{"car_addition_id"=>"10"}}
h.map{|k,v| v.values}.flatten
=> ["9", "10"]
or as per #gotva
h.flat_map{|k,v| v.values}
{"0"=>{"car_addition_id"=>"9"}, "1"=>{"car_addition_id"=>"10"}}
.values.flat_map(&:values)
# => ["9", "10"]
Assuming you want the values associated with the key "car_addition_id" (and not whatever key appears in those locations in the expression), the most straightforward way is:
h.map { |_k,v| v["car_addition_id"] }
#=> ["9", "10"]
This of course works if the inner hashes contain multiple key/value pairs.
For those unfamiliar with the notation, _k and _ are both placeholders for an unused block variable; the former can be used to indicate how the block variable would have been written had it been used.

Ruby - Array of Hashes, Trying to Select Multiple Keys and Group By Key Value

I have a set of data that is an array of hashes, with each hash representing one record of data:
data = [
{
:id => "12345",
:bucket_1_rank => "2",
:bucket_1_count => "12",
:bucket_2_rank => "7",
:bucket_2_count => "25"
},
{
:id => "45678",
:bucket_1_rank => "2",
:bucket_1_count => "15",
:bucket_2_rank => "9",
:bucket_2_count => "68"
},
{
:id => "78901",
:bucket_1_rank => "5",
:bucket_1_count => "36"
}
]
The ranks values are always between 1 and 10.
What I am trying to do is select each of the possible values for the rank fields (the :bucket_1_rank and :bucket_2_rank fields) as keys in my final resultset, and the values for each key will be an array of all the values in its associated :bucket_count field. So, for the data above, the final resulting structure I have in mind is something like:
bucket 1:
{"2" => ["12", "15"], "5" => ["36"]}
bucket 2:
{"7" => ["25"], "9" => ["68"]}
I can do this working under the assumption that the field names stay the same, or through hard coding the field/key names, or just using group_by for the fields I need, but my problem is that I work with a different data set each month where the rank fields are named slightly differently depending on the project specs, and I want to identify the names for the count and rank fields dynamically as opposed to hard coding the field names.
I wrote two quick helpers get_ranks and get_buckets that use regex to return an array of fieldnames that are either ranks or count fields, since these fields will always have the literal string "_rank" or "_count" in their names:
ranks = get_ranks
counts = get_counts
results = Hash.new{|h,k| h[k] = []}
data.each do |i|
ranks.each do |r|
unless i[r].nil?
counts.each do |c|
results[i[r]] << i[c]
end
end
end
end
p results
This seems to be close, but feels awkward, and it seems to me there has to be a better way to iterate through this data set. Since I haven't worked on this project using Ruby I'd use this as an opportunity to improve my understanding iterating through arrays of hashes, populating a hash with arrays as values, etc. Any resources/suggestions would be much appreciated.
You could shorten it to:
result = Hash.new{|h,k| h[k] = Hash.new{|h2,k2| h2[k2] = []}}
data.each do |hsh|
hsh.each do |key, value|
result[$1][value] << hsh["#{$1}_count".to_sym] if key =~ /(.*)_rank$/
end
end
puts result
#=> {"bucket_1"=>{"2"=>["12", "15"], "5"=>["36"]}, "bucket_2"=>{"7"=>["25"], "9"=>["68"]}}
Though this is assuming that :bucket_2_item_count is actually supposed to be :bucket_2_count.

Resources