From: Greg Hauptmann Date: 2008-10-28T09:42:39+09:00 Subject: Re: Hash.merge_add! extension - how does this code look? thanks all for feedback to date - here's my latest take ------------------------------------ def merge_add!(h) raise "Parameter passed in not a hash" if !h.instance_of?(Hash) # normalise input hash to contain arrays h.each { |key, value| if !value.instance_of?(Array) then h[key] = [value] end } self.merge!(h) do |key, existing, new| existing = existing.instance_of?(Array) ? existing : [existing] existing + new end end ------------------------------------ Macintosh-2:myequity greg$ cat spec/lib/hash_extensions_spec.rb require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe "merge_add!" do before(:each) do @empty_hash = {} @hash_with_number = {100 => 1} @hash_with_array = {100 => [1]} end it "should raise an error if there is no object passed to the method" do lambda{ @empty_hash.merge_add!() }.should raise_error(ArgumentError) end it "should raise an error if the object passed is not a Hash" do lambda{@empty_hash.merge_add!(123)}.should raise_error(RuntimeError, "Parameter passed in not a hash") end it "should raise an error if the number of items in the hash in not 1" do lambda{@empty_hash.merge_add!({101 => 2, 102 => 3})}.should_not raise_error end it "should create a new Array & wrap new value as an array" do @empty_hash.merge_add!( {101 => 2} ) @empty_hash[101].should eql([2]) end it "should create a new Array & use the array passed as the value" do @empty_hash.merge_add!( {101 => [2]} ) @empty_hash[101].should eql([2]) end it "should migrate existing value to an Array, then add the new value, if existing value is NOT an array" do @hash_with_number.merge_add!( {100 => 2} ) @hash_with_number[100].should eql([1, 2]) end it "should migrate add the new value to existing, if existing value IS an array" do @hash_with_array.merge_add!( {100 => 2} ) @hash_with_array[100].should eql([1, 2]) end end On Mon, Oct 27, 2008 at 5:41 PM, Greg Hauptmann wrote: > I just noticed the code suggested might have a problem when the > original hash is empty. That is using the ability to pass a block to > 'merge!" is good, but when the source hash is empty it does not seem > to trigger use of the code in the block to merge. > > -----code---- > def merge_add!(h) > self.merge!(h) do |key, existing, new| > new = new.instance_of?(Array) ? new : [new] > existing = existing.instance_of?(Array) ? existing : [existing] > existing + new > end > end > ------------- > ---in console--- > ?> {}.merge_add!({1 => 100}) > => {1=>100} <<== DID NOT PUT THE '100' IN AN ARRAY!! > >>> {1 => 300}.merge_add!({1 => 100}) > => {1=>[300, 100]} > -------------- > > regards > Greg > > On Mon, Oct 27, 2008 at 5:21 PM, Stefan Rusterholz wrote: >> >> Greg Hauptmann wrote: >> > thanks for highlighting this! Is the quickest way to normalise to Array >> > via >> > the following? >> > hashitem.instance_of?(Array) ? hashitem : [hashitem] >> >> If you're building up the hash yourself then I'd use the following >> idiom: >> collector = Hash.new { |h,k| h[k] = [] } >> collector['key1'] << 'value1' >> collector['key2'] << 'value1' >> collector['key1'] << 'value2' >> p collector >> >> Simplifies adding to and reading from the hash. But be aware that with >> the hash having a different default value than nil, you can't use 'if >> collector[key] then' to test for existence of a key anymore, you have to >> use has_key?. >> >> If you're not building the hash up yourself, then yes, I'd use what you >> wroten in an each loop and override the existing value. >> >> Regards >> Stefan >> -- >> Posted via http://www.ruby-forum.com/. >> > >