Feature #12165
openHash#first, Hash#last
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) over 8 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 whenst_table
got keeping the insertion order I proposedHash#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.