darthjee/core_ext

View on GitHub
lib/darthjee/core_ext/hash/squasher.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

module Darthjee
  module CoreExt
    module Hash
      # @api private
      #
      # @author Darthjee
      #
      # class responsible for squashing a hash
      #
      # @see Transformable#squash
      # @see Transformable#to_deep_hash
      #
      # @example (see Transformable#squash)
      # @example (see #squash)
      class Squasher
        attr_reader :joiner

        # @param joiner [::String] string used to join keys
        def initialize(joiner = '.')
          @joiner = joiner
        end

        # Squash a hash creating a new hash
        #
        # Squash the hash so that it becomes a single level
        # hash merging the keys of outter and inner hashes
        #
        # @param hash [::Hash] hash to be squashed
        #
        # @return [::Hash]
        #
        # @example Simple usage
        #   hash = {
        #     person: [{
        #       name: %w[John Wick],
        #       age: 22
        #     }, {
        #       name: %w[John Constantine],
        #       age: 25
        #     }]
        #   }
        #
        #   squasher = Darthjee::CoreExt::Hash::Squasher.new
        #
        #   squasher.squash(hash) # changes hash to {
        #                         #   'person[0].name[0]' => 'John',
        #                         #   'person[0].name[1]' => 'Wick',
        #                         #   'person[0].age'  => 22,
        #                         #   'person[1].name[0]' => 'John',
        #                         #   'person[1].name[1]' => 'Constantine',
        #                         #   'person[1].age'  => 25
        #                         # }
        #
        # @example Custom joiner
        #   hash = {
        #     person: {
        #       name: 'John',
        #       age: 22
        #     }
        #   }
        #
        #   squasher = Darthjee::CoreExt::Hash::Squasher.new('> ')
        #
        #   squasher.squash(hash) # changes hash to {
        #                         #   'person> name' => 'John',
        #                         #   'person> age'  => 22
        #                         # }
        def squash(hash)
          hash.keys.each do |key|
            next unless hash[key].is_any?(Hash, Array)

            value = hash.delete(key)
            add_value_to_hash(hash, key, value)
          end
          hash
        end

        private

        # @private
        #
        # Perform squashing on array
        #
        # @param key [::String] key to be prepended on
        #   hash keys
        # @param array [::Array] array to be squashed
        #
        # @return [::Hash] hash with indexed keys
        def squash_array(key, array)
          array.map.with_index.inject({}) do |hash, (element, index)|
            new_key = "#{key}[#{index}]"
            add_value_to_hash(hash, new_key, element)
          end
        end

        # @private
        #
        # Add positioned values to a hash
        #
        # @param hash [::Hash] hash to receive the values
        # @param key [::String] String to be prepended
        #
        # @overload add_value_to_hash(hash, key, sub_hash)
        #   @param sub_hash [::Hash] subhash to be squashed
        #     key prepended and merged into hash
        #
        # @overload add_value_to_hash(hash, key, array)
        #   @param array [::Array] array to be squashed into
        #     {::Hash} and merged into hash
        #
        # @overload add_value_to_hash(hash, key, object)
        #   @param object [::Object] object to be treated
        #     as value
        #
        # @return [::Hash]
        def add_value_to_hash(hash, key, element)
          case element
          when Hash
            value = squash(element)
            hash.merge! prepend_to_keys(key, value)
          when Array
            hash.merge! squash_array(key, element)
          else
            hash.merge!(key => element)
          end
        end

        # @private
        #
        # Appends prefix to all keys of a hash
        #
        # @param prefix [::String] prefix to be prepended
        # @param hash [::Hash] original hash to me changed
        #   (already squashed)
        #
        # @return [::Hash] new hash already squashed
        def prepend_to_keys(prefix, hash)
          hash.inject({}) do |subhash, (key, value)|
            new_key = [prefix, key].join(joiner)
            subhash.merge!(new_key => value)
          end
        end
      end
    end
  end
end