ruby-on-railsrubyruby-hash

Replace all HASH keys with another key


I'm trying to replace all the keys inside a hash with another key, like this:

{
  name: 'Josh',
  surname: 'Simon',
  cars: [{
    name: 'Diablo',
    seats: [{ name: 'Josh', surname: 'Simon' }],
  },
  {
    name: 'Raptor',
    seats: [{ name: 'Josh', surname: 'Simon' }],
  },
  {
    name: 'Testarossa',
    seats: [{ name: 'Josh', surname: 'Simon' }],
  }],
}

let's say I want to change the cars key to people, so I can achieve something like this

{
  name: 'Josh',
  surname: 'Simon',
  cars: [{
    name: 'Diablo',
    people: [{ name: 'Josh', surname: 'Simon' }],
  },
  {
    name: 'Raptor',
    people: [{ name: 'Josh', surname: 'Simon' }],
  },
  {
    name: 'Testarossa',
    people: [{ name: 'Josh', surname: 'Simon' }],
  }],
}

can this be done somehow?


Solution

  • If the variable hash holds the hash given in the example, and that object is to be mutated, one can write the following.

    hash[:cars].each { |h| h.transform_keys! { |k| k == :seats ? :people : k } }
      #=> [
      #    {
      #     :name=>"Diablo",
      #     :people=>[{:name=>"Josh", :surname=>"Simon"}]
      #    },
      #    {
      #     :name=>"Raptor",
      #     :people=>[{:name=>"Josh", :surname=>"Simon"}]
      #    },
      #    {
      #     :name=>"Testarossa",
      #     :people=>[{:name=>"Josh", :surname=>"Simon"}]
      #    }
      #   ]
    

    ​so now

    hash
      #=> {
      #    :name=>"Josh",
      #    :surname=>"Simon",
      #    :cars=>[
      #      {
      #       :name=>"Diablo",
      #       :people=>[{:name=>"Josh", :surname=>"Simon"}]
      #      },
      #      {
      #       :name=>"Raptor",
      #       :people=>[{:name=>"Josh", :surname=>"Simon"}]
      #      },
      #      {
      #       :name=>"Testarossa",
      #       :people=>[{:name=>"Josh", :surname=>"Simon"}]
      #      } 
      #    ]
      #   }
    

    See Hash#transform_keys!.


    If the hash is not to be mutated you could operate on a deep copy. One of doing that is to use the methods Marshal::load and Marshal::dump:

    h = Marshal.load(Marshal.dump(hash))
      #=> {
      #    :name=>"Josh",
      #    :surname=>"Simon",
      #    :cars=>[
      #      {
      #       :name=>"Diablo",
      #       :seats=>[{:name=>"Josh", :surname=>"Simon"}]
      #      },
      #      {
      #       :name=>"Raptor",
      #       :seats=>[{:name=>"Josh", :surname=>"Simon"}]
      #      },
      #      {
      #        :name=>"Testarossa",
      #        :seats=>[{:name=>"Josh", :surname=>"Simon"}]
      #      }
      #    ]
      #   }
    

    Note that if we alter h by writing, for example,

    h[:name] = "Lola"
    h[:cars][1][:seats][0] = "cats"
    

    we may verify that hash is unchanged.