5

I have a Hash with timestamp as keys.

hash = {
  "2016-05-31T22:30:58+02:00" => {
          "path" => "/",
        "method" => "GET"
  },
  "2016-05-31T22:31:23+02:00" => {
          "path" => "/tour",
        "method" => "GET"
  },
  "2016-05-31T22:31:05+02:00" => {
          "path" => "/contact_us",
        "method" => "GET"
  }
}

I order the collection and get the first pair like this:

hash.sort_by {|k, _| k}.first.first

But how do I remove it?

The delete method requires you to know the exakt spelling of the key. Of course I could return the key and then use it in the delete method, but I was thinking if there was any more straight forward way?

Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232

3 Answers3

10

Also note that shift can be used on the Hash values.

hash.shift

Removes the first key-value pair from the hash. Works on arrays too.

Kelsey Hannan
  • 2,857
  • 2
  • 30
  • 46
9

The method you are looking for is hash.keys it returns an array of the keys:

hash.delete(hash.keys.min)

EDIT: I've updated the answer to reflect that keys must be sorted first, this has been added in the original question and brought up by @Shadwell in comments to this post.

I replaced hash.keys.sort.first for hash.keys.min as suggested by @Cary Swoveland, it is not only more performant but better semantically.

Leonel Galán
  • 6,993
  • 2
  • 41
  • 60
  • You'd need to sort the keys too I think to get the first one in order : `hash.delete(hash.keys.sort.first)` (I appreciate the title says an ordered hash but the question then sorts the keys explicitly) – Shadwell May 31 '16 at 20:57
  • I guess it depends if the keys are sorted or not, but that's not clear in the question. But yeah, you could sort them out too. – Leonel Galán May 31 '16 at 21:03
  • yeah, agreed, it's slightly unclear. Think you have the right approach though in both cases. – Shadwell May 31 '16 at 21:04
  • @Shadwell, why do you have to sort to find the smallest key? – Cary Swoveland May 31 '16 at 23:41
  • I suggest `hash.keys.min` or `hash.min_by(&:first).first` rather than `hash.keys.sort.first`. Sorting should be avoided when only the smallest or largest value is needed. – Cary Swoveland Jun 01 '16 at 16:17
  • Thanks for the suggestion, it definitely looks/reads better and I'll be using it. But why do you say sort should be avoided? As far as I can tell both `.min` and `.sort` depend on `<=>`. – Leonel Galán Jun 01 '16 at 16:33
  • 1
    Finding the minimum is much faster than sorting. Think about it, finding the minimum requires just a single pass through the array. – Cary Swoveland Jun 01 '16 at 20:29
  • I don't know how I missed that! Thanks again for keeping up with me and explaining, you are right, a sort is likely O(n log n), while finding the min is just O(n). – Leonel Galán Jun 02 '16 at 15:05
  • You don't have to state that you have updated your answer. Consider putting attributions in comments, to keep your answers "clean". If (in a comment) you thank the author of a comment for a suggestion and say that you've edited your answer to incorporate it), that's sufficient. – Cary Swoveland Jun 04 '16 at 14:18
2

As the OP has not stated that the hash's keys are in sorted order, we must assume that there is no guarantee that they are.

hash = { "2016-05-31T22:31:05+02:00"=>{ "path"=>"/tour", "method"=>"GET" },
         "2016-05-31T22:30:58+02:00"=>{ "path"=>"/", "method"=>"GET" },
         "2016-05-31T22:31:23+02:00"=>{ "path"=>"/contact_us", "method"=>"GET" } }

First, find the smallest key (the second one):

smallest_key = hash.keys.min
  #=> "2016-05-31T22:30:58+02:00" 

This is obviously more efficient than sorting the keys then taking the smallest.

Because the date-time strings are in iso8601 format, they can be sorted as strings, without having to first convert them to time objects.

Then use Hash#reject to obtain the desired hash:

hash.reject { |k,_| k == smallest_key }
  #=> {"2016-05-31T22:31:05+02:00"=>{"path"=>"/tour", "method"=>"GET"},
  #    "2016-05-31T22:31:23+02:00"=>{"path"=>"/contact_us", "method"=>"GET"}} 

To change hash in place, write

hash.delete(smallest_key }
hash
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • Don't loop to find the entry matching the key, that's O(n), if you have the key, simply use it, access to hashes is O(1). – Leonel Galán Jun 01 '16 at 14:11
  • Thanks, @Leito. That's a good suggestion for the case where the hash is to be mutated. (I did an edit.) If the hash is not to be mutated (the working assumption), one could write `h = hash.dup; h.delete(smallest_key); h`, but I prefer `reject`, which reads better (imo), does not require a non-block variable, is one line versus three and is probably not much worse performance-wise. – Cary Swoveland Jun 01 '16 at 15:13
  • You are right, it depends on the desired result, mutateted hash or not. Performance depends on n, for smaller n's probably not, for bigger n's yes. – Leonel Galán Jun 01 '16 at 15:28
  • Downvoter: note that the OP does not reorder the hash's keys. – Cary Swoveland Jun 01 '16 at 20:04