简体   繁体   中英

Delete nested hash according to key => value

I have this hash:

response = '{"librairies":[{"id":1,"books":[{"id":1,"qty":1},{"id":2,"qty":3}]},{"id":2,"books":[{"id":1,"qty":0},{"id":2,"qty":3}]}]}'

in which I'd like to delete every librairies where, at least, one of the book quantity is null.

For instance, with this given response , I'd expect this return:

'{"librairies":[{"id":1,"books":[{"id":1,"qty":1},{"id":2,"qty":3}]}]}'

I've tried this:

parsed = JSON.parse(response)

parsed["librairies"].each do |library|
  library["books"].each do |book|
    parsed.delete(library) if book["qty"] == 0
  end
end

but this returns the exact same response hash, without having deleted the second library (the one with id => 2).

You can use Array#delete_if and Enumerable#any? for this

# Move through each array element with delete_if
parsed["librairies"].delete_if do |library|
  # evaluates to true if any book hash in the library
  # has a "qty" value of 0
  library["books"].any? { |book| book["qty"] == 0 }
end

Hope this helps

To avoid changing the hash parsed , you could do the following.

Firstly, let's format parsed so we can see what we're dealing with:

parsed = { "libraries"=>[ { "id"=>1,
                            "books"=>[ { "id"=>1, "qty"=>1 },
                                       { "id"=>2, "qty"=>3 } ]
                          },
                          { "id"=>2,
                            "books"=>[ { "id"=>1, "qty"=>0 },
                                       { "id"=>2, "qty"=>3 } ]
                          }
                        ]
         }

Later I want to show that parsed has not been changed when we create the new hash. An easy way of doing that is to compute a hash code on parsed before and after, and see if it changes. (While it's not 100% certain that different hashes won't have the same hash code, here it's not something to lose sleep over.)

parsed.hash
  #=> 852445412783960729

We first need to make a "deep copy" of parsed so that changes to the copy will not affect parsed . One way of doing that is to use the Marshal module:

new_parsed = Marshal.load(Marshal.dump(parsed))

We can now modify the copy as required:

new_parsed["libraries"].reject! { |h| h["books"].any? { |g| g["qty"].zero? } }
  #=> [ { "id"=>1,
  #       "books"=>[ { "id"=>1, "qty"=>1 },
  #                  { "id"=>2, "qty"=>3 }
  #                ]
  #     }
  #   ]

new_parsed # => { "libraries"=>[ { "id"=>1,
                                   "books"=>[ { "id"=>1, "qty"=>1},
                                              { "id"=>2, "qty"=>3}
                                            ]
                                 }
                               ]
                } 

And we confirm the original hash was not changed:

parsed.hash
  #=> 852445412783960729

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM