From: Robert Klemme Date: 2012-12-10T21:22:17+09:00 Subject: Re: Help me improve Hash#rekey On Sun, Dec 9, 2012 at 7:19 PM, Intransition wrote: > The note `TODO: Improve Hash#rekey code!!!` has been in my docs for too > long. I could use other's insights and thought others might enjoy the > challenge. So here's the code: I think the problem is in the API. That's what makes it too complex. There are too many cases of specific handling and options (see below): > require 'facets/na' > > class Hash > > # Rekey a hash: > # > # rekey() > # rekey(from_key => to_key, ...) > # rekey{|from_key| to_key} > # rekey{|from_key, value| to_key} > # > # If a key map is given, then the first key is changed to the second > key. > # > # foo = { :a=>1, :b=>2 } > # foo.rekey(:a=>'a') #=> { 'a'=>1, :b=>2 } > # foo.rekey(:b=>:x) #=> { :a =>1, :x=>2 } > # foo.rekey('foo'=>'bar') #=> { :a =>1, :b=>2 } > # > # If a block is given, converts all keys in the Hash accroding to the > # given block procedure. If the block returns +NA+ for a given key, > # then that key will be left intact. 1. Unnecessary option: if the key is supposed to stay intact the block should just return the original key. > # > # foo = { :name=>'Gavin', :wife=>:Lisa } > # foo.rekey{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa } > # foo #=> { :name =>"Gavin", :wife=>:Lisa } > # > # If no key map or block is given, then all keys are converted > # to Symbols. 2. Why that default? In my mind this is too much implicit logic. Also this can be easily achieved with hash.rekey(&:to_sym) > # > # Note that if both a +key_map+ and a block are given, the +key_map+ is > # applied first then the block. 3. I would change that to EITHER block OR map argument, but not both. > Not the use of `facets/na`. That is defined as: > > class << NA = ArgumentError.new > def inspect ; 'N/A' ; end > def method_missing(*); self; end > end > > But it is really nothing more than a dummy object used to mean Not > Applicable. So in the case of #rekey, if the block returns NA then the key > goes unchanged. Thinking about it again now, it's probably unnecessary, but > I had wanted a way to say "leave it alone" while also making sure that `nil` > could still be used as a key (even if that's rare). Feel free to remove the > NA business, but if you do please explain why you think its not needed. If one needs a special key one can use a Symbol for that as efficiently as nil. nil is the value return if something is absent and I believe it does not make for a good key in a Hash. > Best solution will get their name put in front of CREDITs for the next > release of Facets. :-) class Hash def rekey(mapping = nil, &convert) c = convert || mapping dup.tap do |h| # preserve type and defaults h.clear each_pair {|k, v| h[c[k] || k] = v} end end end Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/