Home > Back-end >  Ruby/Rails return hash element if hash value is a string
Ruby/Rails return hash element if hash value is a string

Time:09-29

In my Rails 6 and Ruby 2.7 app I'm trying to return only these hash elements which hash value is a string. The catch is that this hash has other hashes in it (so it's hash of hashes). Like below:

hash = {
  language: 'EN',
  details: {
    resultRanges: {
      book: {
        title: 'Dunder Mifflin',
        available: true,
        rank: -1.0,
        label: 'gray',
      },
    },
  },
}

From that hash I should get something like:

{
  language: 'EN',
  details: {
    resultRanges: {
      book: {
        title: 'Dunder Mifflin',
        label: 'gray',
      },
    },
  },
}

I've no clue how to do it. If I use hash.keys I'll get [:language, :details] so I cannot use something like hash.select { |k, v| v.is_a?(String) } because it will give me only the first hash key - {:language=>"EN"}.

CodePudding user response:

What you need here is a recursion, i.e. a function, calling itself:

def filter_hash(hash)
  hash.each_with_object({}) do |(key, value), acc|
    acc[key] = value if value.is_a?(String)
    acc[key] = filter_hash(value) if value.is_a?(Hash)
  end
end

This function checks if the value of your hash is String, then it saves it, if it's a Hash it calls itself with that nested Hash

CodePudding user response:

You're looking for a recursive function.

def recursively_delete(hash)
  hash.each do |k, v|
    if v.is_a?(Hash)
      recursively_delete(v)
    elsif !v.is_a?(String)
      hash.delete(k)
    end
  end
end

recursively_delete(hash)
p hash

=> {:language=>"EN", :details=> {:resultRanges=>{:book=>{:title=>"Dunder Mifflin", :label=>"gray"}}}}
  • Related