1

I have this hash :

{ "car": [
  { "key": 'removeMe1', "name": 'ok' },
  { "key": 'dontRemoveMe1', "surname": 'ok' },
  { "key": 'dontRemoveMe2',
    "array": [
      { "trucks": [
        { "key": 'removeMe2', "name": 'my_profile_name' }
      ] },
      { "trucks": [
        { "key": 'dontRemoveMe3', "name": '34' },
        { "key": 'removeMe3', "surname": '5324' }
      ]}
  ] }
]}

And I would like to remove hash that contains removeMe*

{ "car": [
  { "key": 'dontRemoveMe1', "surname": 'ok' },
  { "key": 'dontRemoveMe2',
    "array": [
      { "trucks": [
        { "key": 'dontRemoveMe3', "name": '34' }
      ] }
  ] }
]}

I succeed at the first level if I do

my_hash.delete_if { |h| ['removeMe1'].exclude?(h['key']) }

But I don't know how to do it for nested levels.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Mio
  • 1,412
  • 2
  • 19
  • 41

2 Answers2

3

You can use the following solution:

def remove_me!(obj)
  case obj
  when Hash
    obj.each_value { |e| remove_me!(e) }
  when Array
    obj.reject! do |e| 
      e.is_a?(Hash) && e.values.any? do |v| 
        v.is_a?(String) && v.start_with?('removeMe') 
      end
    end

    obj.each { |e| remove_me!(e) }
  end
end
remove_me!(hash)
#=> {:car=>[{:key=>"dontRemoveMe1", :surname=>"ok"},
#           {:key=>"dontRemoveMe2", :array=>[{:trucks=>[]}, {:trucks=>[{:key=>"dontRemoveMe3", :name=>"34"}]}]}]}

but note, that this method mutates the original object. You can use dup to create a new one.

The question was only about hashes, but you can also add a part with checks the empty arrays and delete them.

Ilya
  • 13,337
  • 5
  • 37
  • 53
  • OP wants to remove the whole `Hash` if the values include `/\AremoveMe\d+/` – engineersmnky May 12 '17 at 14:25
  • Thanks @llya your solution is more strong but my schema will not be more complexe. So I will validate the other solution even if yours is correct. – Mio May 12 '17 at 16:16
3
def delete_by_key(data)
  data.each do |k, v|
    if Array === v
      v.each{ |i| delete_by_key(i) }
      v.delete_if { |i| i[:key] && i[:key]["removeMe"] or i.empty? }
      data.delete(k) if v.empty?
    end
  end
end

The result is following

data = { 
  "car": [
    { "key": 'removeMe1', "name": 'ok' },
    { "key": 'dontRemoveMe1', "surname": 'ok' },
    { "key": 'dontRemoveMe2',
      "array": [
        { "trucks": [
          { "key": 'removeMe2', "name": 'my_profile_name' }
        ] },
        { "trucks": [
          { "key": 'dontRemoveMe3', "name": '34' },
          { "key": 'removeMe3', "surname": '5324' }
        ]}
    ] 
  }
]}

delete_by_key(data)
#=> {:car=>[{:key=>"dontRemoveMe1", :surname=>"ok"}, {:key=>"dontRemoveMe2", :array=>[{:trucks=>[{:key=>"dontRemoveMe3", :name=>"34"}]}]}]}
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • Very specific solution. What about hashes like `{key1: 'removeMe1' }` or, let's say, array which contains array? – Ilya May 12 '17 at 15:05
  • 1
    @Ilya your concerns are valid, but do we really need all those cases? Do we really need a generic solution here? Do we really need to have a more general solution at a price of more complexity? I don't know. I look at the data and I see some patterns in it. And I assume that these patterns could be the only case and I design a solution that will solve this particular problem. – fl00r May 12 '17 at 15:13
  • Ouhao thanks. I think I need to add a step to remove empty array and parent hash – Mio May 12 '17 at 15:35
  • Any idea to delete also the parent hash when having empty array inside ? – Mio May 16 '17 at 14:44