Feature #21615
closedIntroduce `Array#values`
Description
Motivation¶
In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. Hash exposes #values, but Array does not, which pushes developers toward is_a?/respond_to? branching.
Following the Principle of Least Surprise, users may reasonably expect Array#values to exist because:
- Both
ArrayandHashalready implement#values_at. -
Hashimplements#valuesbutArraydoes not.
Example¶
Today:
def normalize_records(input)
items = input.respond_to?(:values) ? input.values : input
items.each do |item|
do_stuff_with_item(item)
end
end
With Array#values:
def normalize_records(input)
input.values.each do |item|
do_stuff_with_item(item)
end
end
Proposal¶
Add Array#values, returning a copy of the elements of self.
This yields a uniform interface for Array and Hash values without type checks.
Alternatives considered¶
-
Enumerable#values: defaulting toto_a, but I found it too broad of a change. -
Array#each_value: redundant asArray#eachalready covers iteration.
Updated by nobu (Nobuyoshi Nakada) about 2 months ago
matheusrich (Matheus Richard) wrote:
Add
Array#values, returningself. Implementation could simply alias toitself:
I think it should be Array.new(*self).
Hash#values returns a new Array, non-frozen and independent from the receiver.
Updated by nobu (Nobuyoshi Nakada) about 2 months ago
ยท Edited
matheusrich (Matheus Richard) wrote:
Following the Principle of Least Surprise, users may reasonably expect
Array#valuesto exist because:
"PoLS" is a word not to say at a proposal.
That word is negative to convince us.
We are not sure who started to say the word, but Ruby itself hasn't advertised it.
With
Array#values:def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end
Why not to introduce Array#each_value?
Updated by matheusrich (Matheus Richard) about 2 months ago
I think it should be Array.new(*self).
I can do that, sure.
That word is negative to convince us.
Noted! Good to know.
Why not to introduce Array#each_value?
I think it feels clunkier than array.values.each, but is an alternative I considered. IMO it still makes me expect that a #values method would exist since it would be a word present in values_at, fetch_values and each_value.
IMO both could be added, so Array has an interface even more similar to Hash, but I decided to keep this small.
Updated by matheusrich (Matheus Richard) about 2 months ago
- Description updated (diff)
Updated by Dan0042 (Daniel DeLorme) about 2 months ago
I understand the idea of making core collection classes ducktype-compatible, but in that case this proposal should also include Set#values
Updated by matheusrich (Matheus Richard) about 2 months ago
@Dan0042 I considered that. I thought it would be too much for one PR. I'll wait for more feedback, and if people are positive about this, I'll propose Set#values too.
Updated by brightraven (Billy G. Jaime Beltran) 26 days ago
matheusrich (Matheus Richard) wrote in #note-6:
@Dan0042 I considered that. I thought it would be too much for one PR. I'll wait for more feedback, and if people are positive about this, I'll propose
Set#valuestoo.
One principle I have enjoyed heavily from other programming languages that use this type of duck typing is the idea of a higher abstraction called a sequence[1] over which anything can be iterated, be it a set, a hash or an array. It would be pleasant to have this for all collections equally.
Updated by mame (Yusuke Endoh) 25 days ago
This feels like opening Pandora's box. If this is accepted, I can foresee the following discussions arising, just off the top of my head:
- Should
Array#keysbe defined as a method that returns(0...ary.size).to_a? -
Array#find_indexandHash#keyare (roughly) counterparts. Should they be aliased to each other? - Should
Hash#each_keyandArray#each_indexalso be aliased to each other? - What about order-related operations? Wouldn't we also need methods like
Hash#reverse,Hash#rotate, andHash#sort? (Note thatHash#sortwould be incompatible withEnumerable#sort). - Should methods like
Array#rehashandArray#compare_by_identitybe provided (perhaps as no-ops)? - Wouldn't
Array#defaultandArray#default=also be necessary? - Should operators like
Array#<=be defined to align withHash#<=? - While
Array#transform_valuescould be defined straightforwardly, how shouldArray#transform_keysbehave? - The different meanings of
Array#include?andHash#include?are surprising. - The different meanings of
Array#assocandHash#assocare surprising.
After all, I personally feel that Array and Hash are not inherently polymorphic.
If you want to use them polymorphically, I think you should limit their polymorphic use to #[] only.
Updated by matz (Yukihiro Matsumoto) 18 days ago
- Status changed from Open to Rejected
HI,
- Don't use principle of least surprise as your rationale, since background and assumption vary person to person
- I don't think Hashes can be naturally considered as Arrays with non-integer indexes. You can tell it from Hash#each behavior, for example
- Don't mention principle of least surprise in the discussion here
Matz.