Home > Net >  Hash deep transform all values if specific key
Hash deep transform all values if specific key

Time:09-17

My respect to community! I have deep nested hash and I want to transform all values of specific key provided. Something like deep transform values if key == :something Example of hash:

{:id=>"11ed35b8e53c442ea210c39d6f24bddf",
 :createdAt=>"2022-09-16T12:12:55.454Z",
 :updatedAt=>"2022-09-16T12:12:55.454Z",
 :status=>"ACTIVE",
 :description=>"test",
 :goals=>
  [{:Definitions=>[],
    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
    :status=>"ACTIVE",
    :Definitions=>[{:text=>"Search for relics", :status=>"INACTIVE", :id=>"11ec1fc4bd36f876963867013cee2799"}],
    :id=>"11e818be2f0157329c76634ee23c1d8f"}],
 :healthConcernDefinitions=>[]}

I want to transform all values of all keys :status Result must be:

{:id=>"11ed35b8e53c442ea210c39d6f24bddf",
 :createdAt=>"2022-09-16T12:12:55.454Z",
 :updatedAt=>"2022-09-16T12:12:55.454Z",
 :status=>"TRANSFORMED",
 :description=>"test",
 :goals=>
  [{:Definitions=>[],
    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
    :status=>"TRANSFORMED",
    :Definitions=>[{:text=>"Search for relics", :status=>"TRANSFORMED", :id=>"11ec1fc4bd36f876963867013cee2799"}],
    :id=>"11e818be2f0157329c76634ee23c1d8f"}],
 :healthConcernDefinitions=>[]}

I have a couple solutions First one:

hash_to_json = hash.to_json.gsub(/"ACTIVE"|"INACTIVE"/, '"TRANSFORMED"')
JSON.parse(hash_to_json)

Second one:

hash.deep_transform_values { |v| (v == 'ACTIVE' || v == 'INACTIVE') ? 'TRANSFORMED' : v }

Both solutions are working but I do not like to match values, I want to match only specific key and change it all over the hash, somting like:

hash.deep_transform_keys { |k, v| v = 'TRANSFORMED' if k == :status }

Thanks a lot!!!

CodePudding user response:

We could implement a conditional overwrite of a key using an implementation akin to deep_transform_keys.

def deep_overwrite(object, other_h)
  case object
  when Hash
    object.each_with_object({}) do |(key, value), result|
      result[key] = other_h[key] || deep_overwrite(value, other_h)
    end
  when Array
    object.map { |e| deep_overwrite(e,other_h) }
  else
    object
  end
end

While this does not take a block format (as shown in your post), that did not seems to be necessary based on your use case. Instead this will simply overwrite the value of any key (nested at any level) contained in the original (object) that is also contained in other_h.

To call this in your case

original = {:id=>"11ed35b8e53c442ea210c39d6f24bddf",
 :createdAt=>"2022-09-16T12:12:55.454Z",
 :updatedAt=>"2022-09-16T12:12:55.454Z",
 :status=>"ACTIVE",
 :description=>"test",
 :goals=>
  [{:text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
    :status=>"ACTIVE",
    :Definitions=>[{:text=>"Search for relics", :status=>"INACTIVE", :id=>"11ec1fc4bd36f876963867013cee2799"}],
    :id=>"11e818be2f0157329c76634ee23c1d8f"}],
 :healthConcernDefinitions=>[]}

result = deep_overwrite(original,{status: 'TRANSFORMED'})
#=> {:id=>"11ed35b8e53c442ea210c39d6f24bddf",
# :createdAt=>"2022-09-16T12:12:55.454Z",
# :updatedAt=>"2022-09-16T12:12:55.454Z",
# :status=>"TRANSFORMED",
# :description=>"test",
# :goals=>
#  [{:text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
#    :status=>"TRANSFORMED",
#    :Definitions=>
#     [{:text=>"Search for relics",
#       :status=>"TRANSFORMED",
#       :id=>"11ec1fc4bd36f876963867013cee2799"}],
#    :id=>"11e818be2f0157329c76634ee23c1d8f"}],
# :healthConcernDefinitions=>[]}

CodePudding user response:

Unfortunely Hash#deep_transform_values takes in block only values, not keys. And unfortunely Hash#deep_merge and Hash#deep_merge! are not so powerful

You can write your own method(s). It is just idea, you can improve it

class Hash
  def deep_replace(hash)
    deep_dup.deep_replace!(hash)
  end

  def deep_replace!(hash)
    each do |key, value|
      hash.each do |k, v|
        self[key] = v if key == k
      end

      case value
      when Hash
        value.deep_replace!(hash)
      when Array
        value.map! { |v| v.deep_replace!(hash) }
      end
    end
  end
end
hash = {:id=>"11ed35b8e53c442ea210c39d6f24bddf",
 :createdAt=>"2022-09-16T12:12:55.454Z",
 :updatedAt=>"2022-09-16T12:12:55.454Z",
 :status=>"ACTIVE",
 :description=>"test",
 :goals=>
  [{:Definitions=>[{:text=>"Search for relics", :status=>"INACTIVE", :id=>"11ec1fc4bd36f876963867013cee2799"}],
    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
    :status=>"ACTIVE",
    :id=>"11e818be2f0157329c76634ee23c1d8f"}],
  :healthConcernDefinitions=>[]}

And after that you can apply it

# Replace one key
hash.deep_replace(status: "TRANSFORMED")

# => {:id=>"11ed35b8e53c442ea210c39d6f24bddf",
# :createdAt=>"2022-09-16T12:12:55.454Z",
# :updatedAt=>"2022-09-16T12:12:55.454Z",
# :status=>"TRANSFORMED",
# :description=>"test",
# :goals=>
#  [{:Definitions=>[{:text=>"Search for relics", :status=>"TRANSFORMED", :id=>"11ec1fc4bd36f876963867013cee2799"}],
#    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
#    :status=>"TRANSFORMED",
#    :id=>"11e818be2f0157329c76634ee23c1d8f"}],
#  :healthConcernDefinitions=>[]}
# Replace few keys
hash.deep_replace(status: "TRANSFORMED", txid: "555", id: "99999999999999999999999999999999")

# => {:id=>"99999999999999999999999999999999",
# :createdAt=>"2022-09-16T12:12:55.454Z",
# :updatedAt=>"2022-09-16T12:12:55.454Z",
# :status=>"ACTIVE",
# :description=>"test",
# :goals=>
#  [{:Definitions=>[{:text=>"Search for relics", :status=>"TRANSFORMED", :id=>"99999999999999999999999999999999"}],
#    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
#    :status=>"TRANSFORMED",
#    :id=>"99999999999999999999999999999999"}],
#  :healthConcernDefinitions=>[]}
# Check original (it wasn't changed)
hash

# => {:id=>"11ed35b8e53c442ea210c39d6f24bddf",
# :createdAt=>"2022-09-16T12:12:55.454Z",
# :updatedAt=>"2022-09-16T12:12:55.454Z",
# :status=>"TRANSFORMED",
# :description=>"test",
# :goals=>
#  [{:Definitions=>[{:text=>"Search for relics", :status=>"INACTIVE", :id=>"11ec1fc4bd36f876963867013cee2799"}],
#    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
#    :status=>"ACTIVE",
#    :id=>"11e818be2f0157329c76634ee23c1d8f"}],
#  :healthConcernDefinitions=>[]}
# Destructive method
hash.deep_replace!(status: "TRANSFORMED", id: "99999999999999999999999999999999")

# => {:id=>"99999999999999999999999999999999",
# :createdAt=>"2022-09-16T12:12:55.454Z",
# :updatedAt=>"2022-09-16T12:12:55.454Z",
# :status=>"ACTIVE",
# :description=>"test",
# :goals=>
#  [{:Definitions=>[{:text=>"Search for relics", :status=>"TRANSFORMED", :id=>"99999999999999999999999999999999"}],
#    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
#    :status=>"TRANSFORMED",
#    :id=>"99999999999999999999999999999999"}],
#  :healthConcernDefinitions=>[]}
# Check original (it was changed)
hash

# => {:id=>"99999999999999999999999999999999",
# :createdAt=>"2022-09-16T12:12:55.454Z",
# :updatedAt=>"2022-09-16T12:12:55.454Z",
# :status=>"ACTIVE",
# :description=>"test",
# :goals=>
#  [{:Definitions=>[{:text=>"Search for relics", :status=>"TRANSFORMED", :id=>"99999999999999999999999999999999"}],
#    :text=>"Follows Action Plan Appropriately, Worse or Warning Symptoms",
#    :status=>"TRANSFORMED",
#    :id=>"99999999999999999999999999999999"}],
#  :healthConcernDefinitions=>[]}
  • Related