From: Robert Klemme Date: 2004-07-09T00:52:33+09:00 Subject: Re: Proposal for Hash#invert "Han Holl" schrieb im Newsbeitrag news:b93fa83f.0407080440.3ce4f94f@posting.google.com... > Hello, > > The current Hash#invert implementation will silently throw away keys that > have the same value associated with them. > Because I often have many to one hashes, but have preferred keys, I'd like > to propose having an optional arbitrating block, that gets called with > the alternative valuse in an array. Something like this: > > class Hash > def invert(&blk) Don't need the blk as far as I can see. > rsl = {} > arbitrate = {} > each_pair do |k, v| > if rsl.has_key?(v) && block_given? > (arbitrate[v] ||= [rsl[v]]) << k > else > rsl[v] = k > end > end A check for block_given? is missing here: > arbitrate.each_pair do |k,v| > rsl[k] = yield v > end > rsl > end > end > > You can call this with something like: > hash.invert {|x| x.find {|el| fields.include?(el)} || > x.sort_by{|el| el.size}[-1] } > > or even the simple: > hash.invert { raise 'This hash is not invertible' } > > Does this sound generally useful, or is it just me ? It does indeed sound useful but I would implement it differently in order to save the extra hash which is only needed in certain cases. I'd hand over all info and let the block decide what to do with it: class Hash def invert_test(&b) reverse = self.class.new each do |key, val| if b and reverse.has_key? val b.call reverse, val, key else reverse[val] = key end end reverse end end h = { "a" => 1, "b" => 2, "c" => 3, "d" => 3, } puts "original" p h puts "overwrite" p h.invert_test puts "ignore" p h.invert_test {} puts "modify key" p h.invert_test {|hash, key, val| hash["duplicate #{key}"] = val} puts "store in array if more than one value" p h.invert_test {|hash, key, val| oldval = hash[key] case oldval when Array oldval << val else hash[key] = [oldval, val] end } Yields: 17:45:21 [ruby]: ./hash-reverse.rb original {"a"=>1, "b"=>2, "c"=>3, "d"=>3} overwrite {1=>"a", 2=>"b", 3=>"d"} ignore {1=>"a", 2=>"b", 3=>"c"} modify key {"duplicate 3"=>"d", 1=>"a", 2=>"b", 3=>"c"} store in array if more than one value {1=>"a", 2=>"b", 3=>["c", "d"]} As a nice side effect the implementation of #invert becomes simpler. What do you think? Kind regards robert