Project

General

Profile

Actions

Feature #12165

open

Hash#first, Hash#last

Added by stillhart (Fabian Stillhart) almost 9 years ago. Updated over 1 year ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:74268]

Description

Just run into a simple problem with a colleague and was wondering why there is no Hash#last method?

{a: true, b: false}.last
NoMethodError: undefined method 'last' for {:a=>true, :b=>false}:Hash

Interestingly I while playing arround I found out that the Hash#first method works. But why is it not in the ruby-doc?

{a: true, b: false}.first
=> [:a, true]

I would assume the Hash#last method would work like the Hash#first method:

{a: true, b: false}.last
=> [:b, false]

If I am not wrong the order of a Hash is always the same when calling Hash#each. So wouldn't it make sense to have Hash#last method?

I tested it in Ruby 2.2.4 and Ruby 2.3.0.

Updated by shevegen (Robert A. Heiler) almost 9 years ago

Yes, I would concur considering that ruby hashes are ordered these days.

It may have been different in the past when the hashes were not ordered.

But the moment when ruby hashes were ordered, I think using .first or .last
would make sense now. (I do remember that apeiros on IRC once said that
one should still not rely on Hashes being ordered; no idea why or why not,
just thought I would mention it.)

The method .last() also seems to work for Strings too:

`"abc".last # => "c"`

The way I do this usually for Hashes is by first calling .keys, then .first
or .last respectively, but a direct method may be more convenient. (Not that
it would be a big deal for me either way, I can live without it too.)

A possible reason may be that it may be not specific, e. g. if you want
the first key or last key, or first key and last value, but this could
be solved too, such as .first_value or .last_value if you know what I mean?

But I assume that, most everyone wants to just get the .first key and .last
key anyway since it is the main pointer.

However had, as your example shows, you want .last and .first to return
both key and value for a Hash. Which is also fine for me, I don't mind
either way, just that it has to be documented properly. :)

Updated by bughit (bug hit) over 1 year ago

Hash should absolutely have a performant last method that doesn't build an intermediate array (hash.(values|keys).last). This is connected to having a performant reverse_each (#8707)

Updated by nobu (Nobuyoshi Nakada) over 1 year ago

stillhart (Fabian Stillhart) wrote:

Interestingly I while playing arround I found out that the Hash#first method works. But why is it not in the ruby-doc?

It is Enumerable#first.

p Hash.instance_method(:first) #=> #<UnboundMethod: Enumerable#first(*)>

Updated by bughit (bug hit) over 1 year ago

@nobu (Nobuyoshi Nakada) Since you commented recently, what do you think about about a performant Hash#last(n=1) and a related performant Hash#reverse_each? Shouldn't any ordered collection that supports bidirectional enumeration, have those?

Updated by nobu (Nobuyoshi Nakada) over 1 year ago

Although I've not thought about Hash#last, once when st_table got keeping the insertion order I proposed Hash#reverse_each (st_reverse_foreach for it precisely) to matz, but was rejected.

Updated by bughit (bug hit) over 1 year ago

nobu (Nobuyoshi Nakada) wrote in #note-5:

Although I've not thought about Hash#last, once when st_table got keeping the insertion order I proposed Hash#reverse_each (st_reverse_foreach for it precisely) to matz, but was rejected.

Is it not reasonable for a user to expect that core/std lib code will be at least not, knowingly, intentionally, horribly sub-optimal? Since at least two people tried to fix Hash#reverse_each and were rejected, that's exactly what it is at this point, knowingly, intentionally, horribly sub-optimal.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0