From: Trans Date: 2008-10-28T20:13:32+09:00 Subject: Re: Hash.merge_add! extension - how does this code look? On Oct 28, 4:38 am, Brian Candler wrote: > Greg Hauptmann wrote: > > interesting - is it really Ruby approach to let things 'break' during > > a method so to speak > > Generally speaking, yes it is. > > Depending on your implementation, you probably only make use of a one or > two Hash methods - says 'keys' and '[]' > > By not testing the class, you allow other objects which also implement > these methods to work, which typically makes your code more useful. > > If you wanted to be really anal, you could do: > >   raise "Unsuitable object (doesn't implement 'keys')" unless >         hash.respond_to?(:keys) >   raise "Unsuitable object (doesn't implement '[]')" unless >         hash.respond_to?(:[]) > > However the error message you would then generate would be almost > identical to the default Ruby one: > > >      "" Nice explanation. > Now, in this particular case, I'd say an ideal API would only depend on > 'each', and then it could be used for any enumerable object which yields > two values. For example: I agree. I should have done this myself, but I started playing golf ;) >   class Hash >     def merge_add!(h) >       h.each do |k,v| >         if has_key?(k) >           self[k] = Array[self[k]] + Array[v] >         else >           self[k] = v self[k] = Array(v) >         end >       end >     end >   end > >   tmp = {"one"=>1, "two"=>2} >   tmp.merge_add!([["three",3], ["one",999]]) >   p tmp > > I should add that I don't like the fact that the resulting hash will > have a mixture of array and non-array values, as it makes the values > harder to use later. If it were me I'd want all the values to be arrays. I think that was the intent. The above noted change might fix (?). > A similar case I have come across before is a multi-valued Hash#invert, > where different keys map to the same value. > >   # input: {"foo"=>1, "bar"=>1, "baz"=>2} > > In that case, what I would want is > >   # output: {1=>["foo","bar"], 2=>["baz"]} > > rather than > >   # output: {1=>["foo","bar"], 2=>"baz"} >> {"foo"=>1, "bar"=>1, "baz"=>2}.invert => {1=>"bar", 2=>"baz"} t