2014-06-24 8 views
7

Enumerable#group_by conserva l'ordine originale all'interno di ciascun valore? Quando ottengo questo:Il gruppo Enumerable conserva l'ordine dell'Enumerabile?

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

è garantito che, per esempio, l'array [1, 3, 5] contiene gli elementi in questo ordine e non, ad esempio [3, 1, 5]?

C'è qualche descrizione riguardo questo punto?

Non sto menzionando l'ordine tra i tasti 1 e 0. Questo è un problema diverso.

+0

'usa Enumerable'' each' a Traverse la collezione. Cambiare l'ordine richiederebbe uno sforzo extra. – Stefan

+0

Ma in precedenza ho appreso che il reminescente 'Enumerable # sort' non è stabile. Quindi non potevo esserne sicuro. – sawa

risposta

11

Sì, Enumerable#group_by conserva l'ordine di input.

Ecco l'attuazione di tale metodo in MRI, da 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 chiamate group_by_i su ogni matrice (obj) elemento in ordine. group_by_i crea una matrice a un elemento (rb_ary_new3(1, i)) la prima volta che viene rilevato un gruppo e successivamente passa all'array (rb_ary_push(values, i)). Quindi l'ordine di input è preservato.

Inoltre, RubySpec lo richiede. Da 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

Dipende dall'implementazione di 'each' che viene mappata tramite' id_each' nella chiamata a 'rb_block_call' – Matt

4

Più in particolare, le chiamate Enumerableeach quindi dipende da come each è implementato e se each rese gli elementi nell'ordine originale:

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

Grazie per aver chiarito che chiama' each'. 'reverse_each' non avrebbe senso se l'ordine di' each' fosse arbitrario. L'esistenza di 'reverse_each' non implica che' each' conservi l'ordine (a meno che non venga sovrascritto dall'utente)? – sawa

+1

È solo un esempio. 'Array # each' ovviamente fornisce gli elementi [nello stesso ordine] (http://www.ruby-doc.org/core-2.1.2/Array.html#class-Array-label-Iterating+over+Arrays): * "Nel caso di ciascuna matrice, tutti gli elementi nell'istanza Array vengono restituiti al blocco fornito in sequenza." * Ma altre classi potrebbero implementarlo in modo diverso, ad esempio 'Hash' in Ruby 1.8. Pertanto, 'group_by' non può garantire alcun ordine, dipende interamente da' each'. – Stefan

Problemi correlati