0

I want to create a hash, that is initialised with the letters 'A'..'Z' and "" on the other hand.

abcliste = {}
('A'..'Z').to_a.each { |letter| abcliste[letter] = ""}
print abcliste # => {"A"=>"", "B"=>"", "C"=>"", ... , "Z"=>""}

But, if I use a class, with the same code, I will get an array ["A", "B", "C", "D", "E", ... , "Z"]:

class Abcliste
  def initialize
    @abcliste = {}
    @abcliste = ('A'..'Z').to_a.each { |letter| @abcliste.store(letter, "") }
    print @abcliste # => ["A", "B", "C", "D", "E", ... , "Z"]
  end
end

a = Abcliste.new # => ["A", "B", "C", "D", "E", ... , "Z"]
  1. Why is this so?
  2. How can I write a class, so that I generates an hash from ('A'..'Z')
Tim Malseed
  • 6,003
  • 6
  • 48
  • 66
  • 3
    " if I use a class, with the **same code**" – Here's a hint: it's not the same code. Compare the differences, and you will find the error. – Jörg W Mittag Jan 18 '17 at 09:36

5 Answers5

2

Your code

You don't need to_a, and you shouldn't overwrite @abcliste :

@abcliste = {}
('A'..'Z').each{ |letter| @abcliste.store(letter,"")}
print @abcliste

Alternatives

each_with_object

The above code can be shortened with each_with_object :

@abcliste = ('A'..'Z').each_with_object({}) { |letter, liste| liste.store(letter,"") }

map

('A'..'Z').map{|letter| [letter, ""] }.to_h
#=> {"A"=>"", "B"=>"", "C"=>"", "D"=>"", "E"=>"", "F"=>"", "G"=>"", "H"=>"", "I"=>"", "J"=>"", "K"=>"", "L"=>"", "M"=>"", "N"=>"", "O"=>"", "P"=>"", "Q"=>"", "R"=>"", "S"=>"", "T"=>"", "U"=>"", "V"=>"", "W"=>"", "X"=>"", "Y"=>"", "Z"=>""}

It first creates an array of pairs (letter and ""), and converts it to a Hash.

hash with default

Yet another possibility would be to just define a Hash with a default value :

@abcliste = Hash.new{|h,k| h[k] = "" }
p @abcliste
#=> {}
@abcliste["A"] << 'letters'
p @abcliste["A"]
#=> "letters"
p @abcliste["B"]
#=> ""
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
1

The problem is that #each will return the array you iterated over. Hence after you assign that to @abcliste, all the #store calls don't matter as you have replaced the value of the instance variable altogether.

Remove the second assignment.

ndnenkov
  • 35,425
  • 9
  • 72
  • 104
1

Here is what happens in your code

@abcliste = {}
@abcliste = ('A'..'Z').to_a.each { |letter| @abcliste[letter] = "" }

Things happen in this order

  • You create {} and assign to @abcliste
  • You use an each loop to store all values in the hash
  • @abcliste has the correct value now, but then …
  • You assign the return value of each to @abcliste — uh oh!

When you execute array.each { ... } the return value of each is the array array.

akuhn
  • 27,477
  • 2
  • 76
  • 91
0

Try Following.

class Abcliste
  def initialize
    @abcliste = {}
    ('A'..'Z').to_a.each { |letter| @abcliste.store(letter, "") }
    print @abcliste
  end
end

a = Abcliste.new 

output:

{"A"=>"", "B"=>"", "C"=>"", "D"=>"", "E"=>"", "F"=>"", "G"=>"", "H"=>"", "I"=>"", "J"=>"", "K"=>"", "L"=>"", "M"=>"", "N"=>"", "O"=>"", "P"=>"", "Q"=>"", "R"=>"", "S"=>"", "T"=>"", "U"=>"", "V"=>"", "W"=>"", "X"=>"", "Y"=>"", "Z"=>""} => #<Abcliste:0x888f9d4 @abcliste={"A"=>"", "B"=>"", "C"=>"", "D"=>"", "E"=>"", "F"=>"", "G"=>"", "H"=>"", "I"=>"", "J"=>"", "K"=>"", "L"=>"", "M"=>"", "N"=>"", "O"=>"", "P"=>"", "Q"=>"", "R"=>"", "S"=>"", "T"=>"", "U"=>"", "V"=>"", "W"=>"", "X"=>"", "Y"=>"", "Z"=>""}
Santosh Sharma
  • 2,114
  • 1
  • 17
  • 28
0

First, I think it is bad practice to use object initialization to do more than load data into a newly create instance. If all you want to do is pass around a function, then you should use a Module and not a Class.

module Abcliste
  def self.for(range, content = '')
    range.each_with_object({}){|element, hash| hash[element] = content}
  end
end

puts Abcliste.for 'A'..'Z'

Will output:

{"A"=>"", "B"=>"", "C"=>"", "D"=>"", "E"=>"", "F"=>"", "G"=>"", "H"=>"", "I"=>"", "J"=>"", "K"=>"", "L"=>"", "M"=>"", "N"=>"", "O"=>"", "P"=>"", "Q"=>"", "R"=>"", "S"=>"", "T"=>"", "U"=>"", "V"=>"", "W"=>"", "X"=>"", "Y"=>"", "Z"=>""}

And this code will also allow you to do this:

puts Abcliste.for 1..10, 'foo'

{1=>"foo", 2=>"foo", 3=>"foo", 4=>"foo", 5=>"foo", 6=>"foo", 7=>"foo", 8=>"foo", 9=>"foo", 10=>"foo"}

ReggieB
  • 8,100
  • 3
  • 38
  • 46
  • Note: if you need to return `hash` every time in your `inject`, you need `each_with_object`. – Eric Duminil Jan 18 '17 at 10:47
  • Good point - though you do need to be careful with `each_with_object` http://stackoverflow.com/a/19064234/1014251 – ReggieB Jan 18 '17 at 11:12
  • Yes - and I'm just about to update my answer to show that. I just wanted to add a note about the difference between `inject` and `each_with_object`. I should have updated, then commented. – ReggieB Jan 18 '17 at 11:16