1

I'm trying to return the lowest result number that it's name is my.name:

{
    "total_results"=>"73", 
    "results"=>
        {
        "28"=> {"description"=>"description_here", "name"=> "my.name"},
        "25"=> {"description"=>"description_here", "name"=> "other1.name"},
        "24"=> {"description"=>"description_here", "name"=> "my.name"},
        "21"=> {"description"=>"description_here", "name"=> "other2.name"}
        }
}

So in the example above it will return 24. I'm fairly new to Ruby so I'm struggling to find a good way to do this.

6 Answers 6

2

Your example is a hash in Ruby

h = {
"total_results"=>"73", 
"results"=>
    {
    "28"=> {"description"=>"description_here", "name"=> "my.name"},
    "25"=> {"description"=>"description_here", "name"=> "other1.name"},
    "24"=> {"description"=>"description_here", "name"=> "my.name"},
    "21"=> {"description"=>"description_here", "name"=> "other2.name"}
    }
}

You can use select to get all matching values, then get min key of them like

h["results"].select{|k,v| v["name"] == "my.name"}.keys.map(&:to_i).min
# => 24
Sign up to request clarification or add additional context in comments.

3 Comments

This is a good answer. However, you can shorten your method chain with min_by(&:to_i) insteap of #map. It will be faster, too.
min_by(&:to_i) returns a string ("24"), that is not what I expected
That's because it converts it for the comparison, but doesn't change the original format. This is usually the Right Thing™ to do, especially when dealing with JSON strings. I'm not trying to bikeshed; I'm just pointing out that you aren't really returning the matching key, you're returning an integer representation of it. I try to avoid unintentional coercions in results, as that might matter to future visitors with similar issues. I'll just leave it at that, since your answer clearly addressed the OPs specific need. Well done!
0

This is a solution. Definitely not the best, but I can at least show you how you can traverse through that hash..

foo = {
"total_results"=>"73", 
"results"=>
    {
    "28"=> {"description"=>"description_here", "name"=> "my.name"},
    "25"=> {"description"=>"description_here", "name"=> "other1.name"},
    "24"=> {"description"=>"description_here", "name"=> "my.name"},
    "21"=> {"description"=>"description_here", "name"=> "other2.name"}
    }
}

bar = nil

foo['results'].each do |k, v|
  if v['name'] == "my.name"
    if bar.nil? || k < bar
      bar = k
    end
  end
end


puts bar  #=> "24"

Comments

0

Try this:

h = {
    "total_results"=>"73", 
    "results"=>
        {
        "28"=> {"description"=>"description_here", "name"=> "my.name"},
        "25"=> {"description"=>"description_here", "name"=> "other1.name"},
        "24"=> {"description"=>"description_here", "name"=> "my.name"},
        "21"=> {"description"=>"description_here", "name"=> "other2.name"}
        }
}

To get the minimum key:

min_key = h["results"].keys.min

To get the min_key value

h["results"][min_key]

Comments

0
h = {
    "total_results"=>"73", 
    "results"=>
        {
        "28"=> {"description"=>"description_here", "name"=> "my.name"},
        "25"=> {"description"=>"description_here", "name"=> "other1.name"},
        "24"=> {"description"=>"description_here", "name"=> "my.name"},
        "21"=> {"description"=>"description_here", "name"=> "other2.name"}
        }
} 

h["results"].select{|key,hash| [key, hash] if hash["name"]=="my.name"}.keys.map(&:to_i).min

=> 24

4 Comments

Your answer is correct but a small change is required, .keys will return array of strings like ["28","24"] and you cannot apply min on string it doesn't give minimum values, If the array consisted of ["28","201","24"].min will return "201". so h["results"].select{|key,hash| [key, hash] if hash["name"]=="my.name"}.keys.map(&:to_i).min
@GouravNaik You are wrong, at least on Ruby 2.3.0. ["28", "24"].min #=> "24" works just fine.
How about ["28", "201", "24"].min ? @CodeGnome
@NirajanPokharel Fair enough. If you're going to consider data sets other than what the OP provided, then you're going to have to coerce to integers for the comparison. But you can still make it shorter and more expressive with min_by(&:to_i) rather than mapping and then calling min on the interim array.
0

A One-Line Solution

Given your posted hash stored in h, you can do the following:

h['results'].select { |k,v| v['name'] == 'my.name' }.keys.min_by(&:to_i)
#=> "24"

Explanation

While this one-liner trades compactness for clarity, it's doing the following:

  1. Sifting results with Enumerable#select for values with a nested hash key of 'name'.
  2. Returning an interim hash containing hashes where the value assigned to the 'name' key contains your desired string value. For example:

    {"28"=>{"description"=>"description_here", "name"=>"my.name"},
     "24"=>{"description"=>"description_here", "name"=>"my.name"}}
    
  3. Using Enumerable#min_by to coerce the keys to integers for the comparison, then find the smallest value and return it in the original format (e.g. as a string).

It only looks like magic. It's just Ruby. :)

2 Comments

syntax error, unexpected '.' .min&.shift ... ^
@Tom I refactored my answer as there was a separate edge case that I hadn't considered, so I've removed the safe navigation operator as the remaining chains don't require them. So, while the original code did work if you had the necessary Ruby version (e.g. MRI Ruby >= 2.3.0), it no longer matters. Happy coding!
0

Find min_key just by a single line of code:

min_key = your_hash['results'].sort.to_h.invert['my.name']

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.