Enumerable的group_by是否保留了Enumerable的顺序?
Enumerable#group_by
是否保留每个值中的原始顺序? 当我得到这个:
[1, 2, 3, 4, 5].group_by{|i| i % 2} # => {1=>[1, 3, 5], 0=>[2, 4]}
是否保证,例如,数组[1, 3, 5]
按此顺序包含元素而不是,例如[3, 1, 5]
?
有关于这一点的描述吗?
我没有提到键1
和0
之间的顺序。 这是一个不同的问题。
是, Enumerable#group_by
保留输入顺序。
以下是MRI中该方法的实现,来自https://github.com/ruby/ruby/blob/trunk/enum.c :
static VALUE enum_group_by(VALUE obj) { VALUE hash; RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); hash = rb_hash_new(); rb_block_call(obj, id_each, 0, 0, group_by_i, hash); OBJ_INFECT(hash, obj); return hash; } static VALUE group_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash)) { VALUE group; VALUE values; ENUM_WANT_SVALUE(); group = rb_yield(i); values = rb_hash_aref(hash, group); if (!RB_TYPE_P(values, T_ARRAY)) { values = rb_ary_new3(1, i); rb_hash_aset(hash, group, values); } else { rb_ary_push(values, i); } return Qnil; }
enum_group_by
调用每个数组( obj
)元素上的group_by_i
。 group_by_i
在第一次遇到一个组时创建一个单元素数组( rb_ary_new3(1, i)
),然后推送到该数组( rb_ary_push(values, i)
)。 因此保留了输入顺序。
此外,RubySpec需要它。 来自https://github.com/rubyspec/rubyspec/blob/master/core/enumerable/group_by_spec.rb :
it "returns a hash with values grouped according to the block" do e = EnumerableSpecs::Numerous.new("foo", "bar", "baz") h = e.group_by { |word| word[0..0].to_sym } h.should == { :f => ["foo"], :b => ["bar", "baz"]} end
更具体地说, Enumerable
调用each
因此它取决于each
实现的方式以及each
是否按原始顺序生成元素:
class ReverseArray < Array def each(&block) reverse_each(&block) end end array = ReverseArray.new([1,2,3,4]) #=> [1, 2, 3, 4] array.group_by { |i| i % 2 } #=> {0=>[4, 2], 1=>[3, 1]}