🗺 HashRemapper

A little lib which maps original keys to the new ones * Build Status

Installation

Add this line to your application's Gemfile:

gem 'hash_remapper'

And then execute:

$ bundle

Or install it yourself as:

$ gem install hash_remapper

Features [*]

  • maps original keys to the new ones 1
  • auto-ignores all the skipped keys 2
  • preprocess a value with a lambda 3
  • allows to remap the keys within preprocessing 4
  • allows to keep data subsets only 5
  • allows to include data with the original key names 6
  • allows to use global context to create composite fields 7
  • merges values if the key already exists and supports #merge 8
  • replaces values if the key already exists and doesn't support #merge 9
  • allows to assign static defaults through lambdas 10
  • allows to remap to the deep values within the context 11
  • allows to create completely new keys (including nested ones) 12

Usage

# Having such a Hash
original_hash = {
    test: 42,
    'data' => [
      1,
      2,
      'string!'
    ],
    ignore: :me,
    nested: {
      hash: :data,
      really: {
        deep: true
      }
    },
    recursive: [
      { number: 21 },
      { number: 42 },
      { test: 13 }
    ]
  }

0: Basic Idea

HashRemapper.remap(
  { a: 1, b: 2 },
  :a => :hello,
  :b => :world
)

# => { hello: 1, world: 2 }

1: Map original keys to the new ones

HashRemapper.remap(
  original_hash,        # pass original Hash
  test: :magic_number,  # List old_key => new_key pairs
  'data' => :data,      # including type change if needed
  ignore: :dont_ignore,
  nested: :internal     # values will be just passed to the new keys
)

# =>
# {
#   magic_number: 42,
#   data: [1, 2, 'string!'],
#   dont_ignore: :me,
#   internal: { hash: :data, really: { deep: true } },
#   recursive: [ { number: 21 }, { number: 42 }, { test: 13 } ]
# }

2: Auto-ignore all the skipped keys

HashRemapper.remap(
  original_hash,
  test: :magic_number
)

# =>
# {
#   magic_number: 42
# }

HashRemapper.remap(
  original_hash,
  false,                    # a flag to pass through the original key => value pairs
  test: :magic_number
)

# =>
#  {
#    "data" => [1, 2, "string!"],
#    :ignore => :me,
#    :magic_number => 42,
#    :nested => {:hash=>:data, :really=>{:deep=>true}}
#    :recursive => [ { number: 21 }, { number: 42 }, { test: 13 } ]
#  }

3: Preprocess the values with lambdas

HashRemapper.remap(
  original_hash,
  test: ->(_, __) { [:test, 21] } # do whatever you want and return "key: value" Array
)

# =>
# {
#   test: 21
# }

4: Remap keys within preprocessing

HashRemapper.remap(
  original_hash,
  test: ->(data, _) { [:magic_number, data.to_s] }
)

# =>
# {
#   magic_number: '42'
# }

5: Keep data subsets only

HashRemapper.remap(
  original_hash,
  'data' => ->(data, _) { ['data', data[0..1]] },
)

# =>
# {
#   'data' => [1, 2]
# }

6: Include data with the original key name

HashRemapper.remap(
  original_hash,
  test: :magic_number,
  ignore: :ignore
)

# =>
# {
#   magic_number: 42,
#   ignore: :me
# }

7: Use global context to create composite fields

HashRemapper.remap(
  original_hash,
  test: ->(data, context) { [:magic_number, data + context['data'][1]] }
)

# =>
# {
#   magic_number: 44
# }

8: Merges values

HashRemapper.remap(
  original_hash,
  test: ->(_, __) { [:magic_number, { one: 1 }] },
  whatever: ->(_, __) { [:magic_number, { two: 2 }] }
)

# =>
# {
#   magic_number: { one: 1, two: 2 }
# }

9: Replace values if the key already exists and doesn't support #merge

HashRemapper.remap(
  original_hash,
  test: ->(_, __) { [:magic_number, 42] },
  whatever: ->(_, __) { [:magic_number, 21] }
)

# =>
# {
#   magic_number: 21
# }

10: Assign static defaults

HashRemapper.remap(
  original_hash,
  test: ->(_, __) { [:magic_number, 21] }
)

# =>
# {
#   magic_number: 21
# }

11: Remap to the deep values

# INFO: For advanced usage see the specs!
HashRemapper.remap(
  original_hash,
  test: [:magic_bool, { path: 'nested.really.deep' }]]
)

# =>
# {
#   magic_bool: true
# }

HashRemapper.remap(
  original_hash,
  test: [:magic_numbers, {path: 'recursive.*.number', strict: false}]]
)

# =>
# {
#   magic_numbers: [21, 42, nil]
# }

12: Create completely new keys (including nested ones)

HashRemapper.remap(
  original_hash,
  test: :magic_number,
  absolutely_new_key: ->(_, __) { [:absolutely_new_key, 'shiny new value'] }
)

# =>
# {
#   magic_number: 42,
#   absolutely_new_key: 'shiny new value'
# }

HashRemapper.remap(
  original_hash,
  _: [[:nested, :new, :key], :test]
)

# =>
# {
#   nested: {
#     new: {
#       key: 42
#     }
#   }
# }


# mapping a deep target from a deep source (BEWARE an old digging API <= v0.1.0)
HashRemapper.remap(
  original_hash,
  _: [[:nested, :new, :key], [:nested,  :really, :deep]]
)

# =>
# {
#   nested: {
#     new: {
#       key: true
#     }
#   }
# }


# mapping a deep target from a deep source (new digging API >= v0.2.0)
HashRemapper.remap(
  original_hash,
  _: [[:new, :deeply, :nested, :value], {path: 'recursive.*.number', strict: false, default: 3.14}]
)

# =>
# {
#   new: {
#     deeply: {
#       nested: {
#         value: [21, 42, 3.14]
#       }
#     }
#   }
# }

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Initial version of this lib was written in pair with @bronislav, so thank him for the invaluable contribution and help.

Bug reports and pull requests are welcome on GitHub at https://github.com/smileart/hash_remapper. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the HashRemapper project’s codebase, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.