1

What should I do to marshal an hash of arrays? The following code only prints {}.

s = Hash.new
s.default = Array.new
s[0] << "Tigger"
s[7] << "Ruth"
s[7] << "Puuh"
data = Marshal.dump(s)
ls = Marshal.restore( data )
p ls

If the hash doesn't contain an array it is restored properly.

Pascal
  • 2,197
  • 3
  • 24
  • 34

4 Answers4

7
s = Hash.new
s.default = Array.new
s[0] << "Tigger"
s[7] << "Ruth"
s[7] << "Puuh"

This code changes the default 3 times (which is probably what showed up in the dump), but it does not store anything in the hash. Try "puts s[8]", it will return [["Tigger"], ["Ruth"], ["Puuh"]].

A Hash.default_proc will do what you want

s = Hash.new{|hash,key| hash[key]=[] }

But you can't marshall a proc. This will work:

s = Hash.new
s.default = Array.new
s[0] += ["Tigger"]
s[7] += ["Ruth"]
s[7] += ["Puuh"]

This works because []+=["Tigger"] creates a new array. An alternative, creating less arrays:

s = Hash.new
(s[0] ||= []) << "Tigger"
(s[7] ||= []) << "Ruth"
(s[7] ||= []) << "Puuh"

Only creates a new array when the key is absent (nil).

steenslag
  • 79,051
  • 16
  • 138
  • 171
  • works for me :-). a little bit more elegant than Julians append_to_hash so this is my answer. – Pascal Mar 31 '10 at 13:02
  • Resetting the default would also work: http://stackoverflow.com/questions/3818623/marshal-ruby-hash-with-default-proc-remove-the-default-proc – Ashitaka Mar 22 '14 at 17:18
2

You can just create your hash structure while initialing Hash.new to avoid such trouble

h = Hash.new{ |a,b| a[b] = [] }
h[:my_key] << "my value"
fl00r
  • 82,987
  • 33
  • 217
  • 237
1

You might be misled about how Hash.default works.

Before you Marshal.dump, print the data structure. It is {}. That's because you are concatenating each string into nil, not into an empty array. The code below illustrates and solves your problem.

s = Hash.new
s.default = Array.new
s[0] = []
s[0] << "Tigger"
s[7] = []
s[7] << "Ruth"
s[7] << "Puuh"
p s
data = Marshal.dump(s)
ls = Marshal.restore( data )
p ls

Returns:

{0=>["Tigger"], 7=>["Ruth", "Puuh"]}
{0=>["Tigger"], 7=>["Ruth", "Puuh"]}

EDIT:

I insert a lot of data into the hash

So maybe some helper code would make the insertion process smoother:

def append_to_hash(hash, position, obj)
  hash[position] = [] unless hash[position]
  hash[position] << obj
end

s = Hash.new
append_to_hash(s, 0, "Tigger")
append_to_hash(s, 7, "Ruth")
append_to_hash(s, 7, "Puuh")
s.default = Array.new // this is only for reading
p s
data = Marshal.dump(s)
ls = Marshal.restore( data )
p ls
Jonathan Julian
  • 12,163
  • 2
  • 42
  • 48
  • Printing the data dump from my example gives "\004\b}\000[\b\"\vTigger\"\tRuth\"\tPuuh" so obviously the data is there but it cannot be used. I tried to avoid a special clause on insertion because I insert a lot of data into the hash. – Pascal Mar 31 '10 at 11:37
  • You can append elements using `+` instead of `<<` method, that way you don't need to initialize each new element. – Mladen Jablanović Mar 31 '10 at 11:41
  • Doesn't work here - gives the same result. Version is NetBeans with jRuby 1.8.6. Could you provide a running example? – Pascal Mar 31 '10 at 11:57
  • edited version works for me. expected something cleaner though. ;-) – Pascal Mar 31 '10 at 12:30
0

As you can't use Marshall.dump on a Hash with a proc for the default element, you could use a slightly more roundabout way of appending to each Array instead of <<:

s = Hash.new
s.default = []
s[0] += [ "Tigger" ]
s[7] += [ "Ruth" ]
s[7] += [ "Pooh" ]
data = Marshal.dump(s)
ls = Marshal.restore(data)
p ls
Arkku
  • 41,011
  • 10
  • 62
  • 84