From: Intransition Date: 2012-12-10T03:19:27+09:00 Subject: Help me improve Hash#rekey ------=_Part_93_24450722.1355077165409 Content-Type: multipart/alternative; boundary="----=_Part_94_2156403.1355077165409" ------=_Part_94_2156403.1355077165409 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit 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: 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. # # 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. # # Note that if both a +key_map+ and a block are given, the +key_map+ is # applied first then the block. # # CREDIT: Trans, Gavin Kistner def rekey(key_map=nil, &block) if !(key_map or block) block = lambda{|k| k.to_sym} end key_map ||= {} hash = dup.replace({}) # to keep default_proc (keys - key_map.keys).each do |key| hash[key] = self[key] end key_map.each do |from, to| hash[to] = self[from] if key?(from) end if block hash2 = dup.replace({}) case block.arity when 2 # TODO: is this condition needed? hash.each do |k, v| nk = block.call(k,v) nk = (NA == nk ? k : nk) hash2[nk] = v end else hash.each do |k, v| nk = block.call(k) nk = (NA == nk ? k : nk) hash2[nk] = v end end else hash2 = hash end hash2 end # Synonym for Hash#rekey, but modifies the receiver in place (and returns it). # # foo = { :name=>'Gavin', :wife=>:Lisa } # foo.rekey!{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa } # foo #=> { "name"=>"Gavin", "wife"=>:Lisa } # # CREDIT: Trans, Gavin Kistner def rekey!(key_map=nil, &block) replace(rekey(key_map, &block)) end end 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. Best solution will get their name put in front of CREDITs for the next release of Facets. ------=_Part_94_2156403.1355077165409 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
The note `TODO: Improve Hash#rekey code!!!` has been in my docs f= or too long. I could use other's insights and thought others might enjoy th= e challenge. So here's the code:

  requ= ire 'facets/na'

  class Hash

    # Rekey a hash:
    #
<= div>    #   rekey()
    #   rekey(f= rom_key =3D> to_key, ...)
    #   rekey{|from_k= ey| 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 =3D { :a=3D>1, :b=3D>2 }
=
    #   foo.rekey(:a=3D>'a')       #= =3D> { 'a'=3D>1, :b=3D>2 }
    #   foo.re= key(:b=3D>:x)        #=3D> { :a =3D>1, :x=3D&g= t;2 }
    #   foo.rekey('foo'=3D>'bar')  #= =3D> { :a =3D>1, :b=3D>2 }
    #
&nb= sp;   # If a block is given, converts all keys in the Hash accroding t= o the
    # given block procedure. If the block returns= +NA+ for a given key,
    # then that key will be left= intact.
    #
    #   foo =3D= { :name=3D>'Gavin', :wife=3D>:Lisa }
    #  = ; foo.rekey{ |k| k.to_s }  #=3D>  { "name"=3D>"Gavin", "wif= e"=3D>:Lisa }
    #   foo       =                #=3D>  { :na= me =3D>"Gavin", :wife=3D>:Lisa }
    #
=     # If no key map or block is given, then all keys are converte= d
    # to Symbols.
    #
    # Note that if both a +key_map+ and a block are given, the += key_map+ is 
    # applied first then the block.
    #
    # CREDIT: Trans, Gavin Kis= tner

    def rekey(key_map=3Dnil, &b= lock)
      if !(key_map or block)
 = ;       block =3D lambda{|k| k.to_sym}
  &nbs= p;   end

      key_map ||=3D {= } 

      hash =3D dup.replace(= {})  # to keep default_proc

    &nb= sp; (keys - key_map.keys).each do |key|
      &nbs= p; hash[key] =3D self[key]
      end
      key_map.each do |from, to|
 = ;       hash[to] =3D self[from] if key?(from)
&nbs= p;     end

      if block=
        hash2 =3D dup.replace({})
=         case block.arity
     =   when 2  # TODO: is this condition needed?
  &nb= sp;       hash.each do |k, v|
     =       nk =3D block.call(k,v)
     =       nk =3D (NA =3D=3D nk ? k : nk)
   = ;         hash2[nk] =3D v
     = ;     end
        else
&n= bsp;         hash.each do |k, v|
   = ;         nk =3D block.call(k)
    =         nk =3D (NA =3D=3D nk ? k : nk)
 =           hash2[nk] =3D v
   =       end
        end
      else
        hash2 = =3D hash
      end

 =     hash2
    end

&= nbsp;   # Synonym for Hash#rekey, but modifies the receiver in place (= and returns it).
    #
    #  = foo =3D { :name=3D>'Gavin', :wife=3D>:Lisa }
   = #   foo.rekey!{ |k| k.to_s }  #=3D>  { "name"=3D>"Gav= in", "wife"=3D>:Lisa }
    #   foo    = ;                   #=3D> &= nbsp;{ "name"=3D>"Gavin", "wife"=3D>:Lisa }
    #=
    # CREDIT: Trans, Gavin Kistner

    def rekey!(key_map=3Dnil, &block)
  =     replace(rekey(key_map, &block))
    e= nd

  end

Not the use= of `facets/na`. That is defined as:

  &= nbsp; class << NA =3D ArgumentError.new
     = ; def inspect ; 'N/A' ; end
      def method_missi= ng(*); self; end
    end

But it is really nothing more than a dummy object used to mean Not Applica= ble. So in the case of #rekey, if the block returns NA then the key goes un= changed. 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 th= e NA business, but if you do please explain why you think its not needed.

Best solution will get their name put in front of C= REDITs for the next release of Facets. 

------=_Part_94_2156403.1355077165409-- ------=_Part_93_24450722.1355077165409--