【问题标题】:Does Enumerable's group_by preserve the Enumerable's order?Enumerable 的 group_by 是否保留了 Enumerable 的顺序?
【发布时间】:2014-08-14 06:14:29
【问题描述】:

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 遍历集合。更改顺序需要额外的努力。
  • 但是之前,我了解到提醒Enumerable#sort不稳定。所以我不能确定。

标签: arrays ruby enumerable


【解决方案1】:

是的,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_igroup_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 映射
【解决方案2】:

更具体地说,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 保留了顺序(除非它被用户覆盖)?
  • 这只是一个例子。 Array#each 当然会产生元素 in the same order“如果是 Array 的 each,Array 实例中的所有元素都会按顺序产生给提供的块。” 但是其他类可以以不同的方式实现它,例如 Ruby 1.8 中的 Hash。所以group_by不能保证任何顺序,完全取决于each
  • 所以,总而言之,group_by 在处理数组时保持顺序,但在其他结构中不一定。
猜你喜欢
  • 2011-06-08
  • 2012-01-22
  • 1970-01-01
  • 2012-04-01
  • 2012-11-29
  • 2021-05-21
  • 2021-03-04
  • 2013-04-01
  • 1970-01-01
相关资源
最近更新 更多