Enumerable的group_by方法是否会保留Enumerable的顺序?

15

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]这样的顺序吗?

有关于这一点的任何描述吗?

我没有提到键10之间的顺序。那是另一个问题。


“Enumerable” 使用 “each” 遍历集合。更改顺序需要额外的努力。 - Stefan
但是之前我了解到,Enumerable#sort 不是稳定的,所以我不能确定它。 - sawa
2个回答

17

是的,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 调用 group_by_i 对每个数组 (obj) 元素进行分组。当遇到新的组时,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

这取决于each的实现,它通过在调用rb_block_call时映射到id_each来执行。 - Matt

8
更具体地说,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]}

感谢您明确指出它调用了 each。如果 each 的顺序是任意的,那么 reverse_each 就没有意义。reverse_each 的存在难道不意味着 each 保留了顺序(除非被用户覆盖)吗? - sawa
2
这只是一个例子。当然,Array#each按照相同的顺序返回元素:*"在数组的每个元素上执行块。"* 但是其他类可能会以不同的方式实现它,例如Ruby 1.8中的Hash。因此,group_by不能保证任何顺序,它完全取决于each - Stefan
2
因此,总的来说,group_by 在处理数组时保留顺序,但在其他结构中不一定如此。 - Toby 1 Kenobi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接