2010-03-21 14 views
5

mam kod:TypeError: nie można przekonwertować ciąg na Integer

class Scene 
    def initialize(number) 
    @number = number 
    end 
    attr_reader :number 
end 

scenes = [Scene.new("one"), Scene.new("one"), Scene.new("two"), Scene.new("one")] 

groups = scenes.inject({}) do |new_hash, scene| 
    new_hash[scene.number] = [] if new_hash[scene.number].nil? 
    new_hash[scene.number] << scene 
end 

Kiedy jestem lauching go otrzymuję błąd:

freq.rb:11:in `[]': can't convert String into Integer (TypeError) 
     from freq.rb:11:in `block in <main>' 
     from freq.rb:10:in `each' 
     from freq.rb:10:in `inject' 
     from freq.rb:10:in `<main>' 

Jeśli zmienię kulisami:

scenes = [Scene.new(1), Scene.new(1), Scene.new(2), Scene.new(1)] 

problem zniknął.

Dlaczego pojawia się komunikat o błędzie w pierwszym przypadku? Dlaczego Ruby decyduje się przekonwertować numer sceny na String na Integer?

I jedno dodatkowe pytanie dotyczące metody "wstrzykiwania". Kiedy Ruby zainicjuje zmienną "new_hash" i jak Ruby może znać typ tej zmiennej?

+1

Btw: możesz robić co chcesz, łatwiej za pomocą 'groups = scenes.group_by (&: Number)' zamiast inject. – sepp2k

+0

Zmienna new_hash jest zainicjowana wartością wtrysku (należy pamiętać, że jest to operacja składania, która ma wartość początkową w akumulatorze). – hurikhan77

Odpowiedz

6

Z.E.D. ma rację. Zobacz Jay Fields' Thoughts: Ruby: inject dla dobrego wyjaśnienia inject przez przykład.

Jak przedstawiono, twój blok zwraca tablicę. Tak więc new_hash w |new_hash, scene| kończy się tą tablicą. Kiedy Ruby próbuje znaleźć indeks tablicy "jeden", zgłasza błąd, ponieważ "jeden" jest ciągiem, a nie liczbą całkowitą.

Wszystko, co musisz zrobić, to zwrócić new_hash jako Z.E.D. pokazał, a otrzymasz coś takiego:

{ 
    "two" => [ 
    #<Scene:0x101836470 @number="two"> 
    ], 
    "one" => [ 
    #<Scene:0x101836510 @number="one">, 
    #<Scene:0x1018364c0 @number="one">, 
    #<Scene:0x101836420 @number="one"> 
    ] 
} 
10

try:

 
groups = scenes.inject({}) do |new_hash, scene| 
    new_hash[scene.number] = [] if new_hash[scene.number].nil? 
    new_hash[scene.number] << scene 
    new_hash 
end 

Ruby zajmuje pustą hash przekazany do inject() i zestawy new_hash do tego. Kiedy blok kończy się, zwracana wartość zostaje użyta do zainicjowania new_hash przy następnym przejściu, to znaczy new_hash gromadzi wynik bloku.

W twoim oryginalnym kodzie nie zwracałeś skrótu, ale tablicę (new_hash [numer sceny] to tablica), a następna pętla Ruby narzekała, ponieważ new_hash [numer sceny] próbował wykonać odnośnik do tablica z wartością ciągu, stąd błąd.

+1

Dlaczego została odrzucona?To jest poprawna odpowiedź. – sepp2k

+0

ponieważ się nie udało – shingara

+0

@shingara: Nie, to się nie udaje. Jego kod działa bezbłędnie i zwraca oczekiwany wynik. – sepp2k

0

Aby wyjaśnić, "w jaki sposób Ruby może znać typ tej zmiennej" i dlaczego próbuje "przekonwertować ciąg na liczbę całkowitą", może warto dokonać korekty: Ruby variables and dynamic typing.

2

dlaczego nie korzystać group_by który jest prawdopodobnie dokładnie, co próbują accomblish?

groups = scenes.group_by(&:number) 
# => {"two"=>[#<Scene:0xb728ade0 @number="two">], 
#  "one"=> 
#  [#<Scene:0xb728ae30 @number="one">, 
#  #<Scene:0xb728ae08 @number="one">, 
#  #<Scene:0xb728ada4 @number="one">]} 

inject to operacja składania i nie jest dokładnie to, co chcesz. Przynajmniej jest to niewygodne w użyciu w ten sposób. merge z blokiem będzie prawdopodobnie odpowiedni, jeśli chcesz zastosować jakiś algorytm podczas łączenia lub grupowania.

0

Wiem, że odpowiedź na to pytanie jest akceptowana, ale nie mogę pomóc, ale publikuję odpowiedź.

groups = scenes.inject({}) { |nh, s| nh.tap {|h| (h[s.number] ||= []) << s } } 
Powiązane problemy