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. 

-- You received this message because you are subscribed to the Google Groups 
ruby-talk-google group. To post to this group, send email to 
[email protected]. To unsubscribe from this group, send email 
to [email protected]. For more options, visit this 
group at https://groups.google.com/d/forum/ruby-talk-google?hl=en

Reply via email to