2014-06-24 1 views
7

Est-ce que Enumerable#group_by conserve l'ordre d'origine dans chaque valeur? Quand je reçois ceci:Le groupe Enumerable conserve-t-il l'ordre d'Enumerable?

[1, 2, 3, 4, 5].group_by{|i| i % 2} 
# => {1=>[1, 3, 5], 0=>[2, 4]} 

est-il garanti que, par exemple, le tableau [1, 3, 5] contient les éléments dans cet ordre, et non, par exemple [3, 1, 5]?

Y a-t-il une description concernant ce point? Je ne mentionne pas l'ordre entre les touches 1 et 0. C'est un problème différent.

+0

'' Enumerable' utilise each' pour parcourir la collection. Changer l'ordre nécessiterait un effort supplémentaire. – Stefan

+0

Mais auparavant, j'ai appris que le 'Enumerable # sort 'reminescent n'est pas stable. Donc, je ne pouvais pas être sûr à ce sujet. – sawa

Répondre

11

Oui, Enumerable#group_by préserve l'ordre de saisie.

est ici la mise en œuvre de cette méthode en IRM, de https://github.com/ruby/ruby/blob/trunk/enum.c: élément

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 appels group_by_i sur chaque tableau (obj) dans l'ordre. group_by_i crée un tableau à un seul élément (rb_ary_new3(1, i)) la première fois qu'un groupe est rencontré et appuie sur le tableau par la suite (rb_ary_push(values, i)). Ainsi, l'ordre d'entrée est préservé.

En outre, RubySpec l'exige. De 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 
+0

Cela dépend de l'implémentation de 'each' qui est mappée via' id_each' dans l'appel de 'rb_block_call' – Matt

4

Plus précisément, Enumerable appels each il dépend de la façon each est mis en œuvre et si each donne les éléments dans l'ordre d'origine:

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]} 
+0

Merci d'avoir précisé qu'elle appelle' each'. 'reverse_each' n'aurait pas de sens si l'ordre de' each' était arbitraire. L'existence de 'reverse_each' n'implique-t-elle pas que' each' préserve l'ordre (à moins qu'il ne soit écrasé par l'utilisateur)? – sawa

+1

C'est juste un exemple. 'Array # each 'donne bien sûr les éléments [dans le même ordre] (http://www.ruby-doc.org/core-2.1.2/Array.html#class-Array-label-Iterating+over+Arrays): * "Dans le cas de Array chacun, tous les éléments de l'instance Array sont cédés en séquence au bloc fourni." * Mais d'autres classes pourraient l'implémenter différemment, par exemple 'Hash' dans Ruby 1.8. Par conséquent, 'group_by' ne peut garantir aucun ordre, cela dépend entièrement de' each'. – Stefan

Questions connexes