Combine Keys in Hash Transformations

Hi, I’m wondering what is the best approach to this problem.

I have an input Hash, and as a result, I want to have a new key, that is a function of several other keys and values.

Question: How I could achieve this with dry-transformer?

input = { foo: 'b', bar: 'd' }
combine(input)
# => { foo: 'b', bar: 'd', zoo: 'bd' }

The function used to combine a sub hash can be anything, like:

def combine(hash)
  res = ''"
    res = hash[:foo] if hash[:foo] == 'b'
    res += hash[:foo] if hash[:zoo] == 'd'
    hash.merge(zoo: res)
end

My Ideas

I got some ideas, but only half-working.

Idea 1.

module Functions
  extend Dry::Transformer::Registry

  def self.set_zoo(hash)
     res = ''"
     res = hash[:foo] if hash[:foo] == 'b'
     res += hash[:zoo] if hash[:zoo] == 'd'
     hash.merge(zoo: res)
  end
end
class ZooTransformation < Dry::Transformer::Pipe
    import Dry::Transformer::HashTransformations

    import Functions

    define! do
      deep_symbolize_keys
      set_zoo
    end
end

It is working, but not very convenient, as for each attribute calculation, I’d need separate method, but also I’d need to always merge hash, even if in case of calculating the value makes more sense to just return this value.

Idea 2

I thought then about adding a new transformation function, that would be general to all above.

module Functions
  extend Dry::Transformer::Registry

  # combines subhash of listed keys using given function, and saves result under the specified new key.
  # @param [Hash] source Hash
  # @param keys [Array] - list of keys to consider
  # @param to [Symbol] - name of key used to save the result under
  # @param with [Proc] - function used to transform value
  # @return [Hash] Transformed hash, merged with given key and calculated value pair.
  #
  def self.combine(hash, keys:, to:, with:)
     subhash = hash.select { |k| keys.include?(k) }
     res = with[&subhash]
     
     hash.merge(to => res)
  end
end
class ZooTransformation < Dry::Transformer::Pipe
    import Dry::Transformer::HashTransformations

    import Functions

    define! do
      deep_symbolize_keys
      combine(
        keys: [:foo, :bar],
        to: :zoo,
        with: ZooCalculator.new.method(:call)
      )
    end
end
class ZooCalculator
    def call(foo:, bar:)
       res = ''"
       res = foo if foo == 'b'
       res += zoo if zoo == 'd'
       res
    end
end

This way I can keep my calculations injectable, I have no restrictions, on where to put the code, and I can keep return values intact while keeping the arguments list readable and explicit.

I wonder what are other ideas on how to do this.

1 Like