简体   繁体   中英

Compare array of keys against predefined array - Ruby

I would like to compare the keys I get back in a JSON response for all results against a pre defined array of keys.

So I have this set of keys

def key_array
 ["key1", "key2", "key3"] 
end

My response from an api call is returned in this format (array of hashes) outputted from @response

{"0"=>
  {"key1"=>"value1",
   "key2"=>"value2",
   "key3"=>"value3"},
 "1"=>
   {"key1"=>"value1",
    "key2"=>"value2",
    "key3"=>"value3"},
 "2"=>
   {"key1"=>"value1",
    "key2"=>"value2",
    "key3"=>"value3"},
 "Status"=>"100"}
}

When I do @response.keys I get

["0", "1", "2"]

I can access each set of keys via its index individually with @response["0"].keys , for example will return

  ["key1", "key2", "key3"]

What I would like to do with the rspec matchers is check that every returned result from the api call has the keys set in key_array

At the moment

expect(@response.keys).to match_array(key_array)

is matching the array of indexes against the key_array I have stored. How do I access the keys from within each index?

@response.each_value do |value|
  expect(value.keys).to match_array(key_array)
end

This will work for you.

required_keys = %w{key1 key2 key3}
@response.each do |key, hash|
  next if (key == "Status")
  expect(hash).to include(*required_keys)
end

You can add an optional message to see which index any failure occured on:

@response.each do |key, hash|
  next if (key == "Status")
  expect(hash).to include(*required_keys), "Failed at #{key}"
end

The next check can be turned into a regular-expression check, in case you've got other non-numeric keys that you want to ignore eg

next if /\D/.match(key) # the \D matches non-digits

If you want call expect method only once, you can do

expect( @response.values.map{|h| h.keys.sort}.uniq ).to eq([%w(key1 key2 key3)])

however it is rather ugly approach

I would firstly harvest incompletes:

incompletes = @response.reduce([]) { |memo, kv| 
  (key_array - kv.last.keys).empty? ? memo : memo << kv.first 
}

And then do checks:

expect(incompletes).to match_array([])

Whether you only need to check:

incompletes = @response.reject { |*kv| (key_array - kv.last.keys).empty? }
expect(incompletes).to match_array([]) 

This former approach makes it easy to collect any desired information about wrong entries, since you are free to put anything valuable in the resulting array. Hope it helps.

UPD With status:

incompletes = @response.reject { |k,v| 
# ⇓⇓ is k integer? ⇓⇓  or  ⇓⇓ are valid keys all presented? ⇓⇓
   k.to_i.to_s != k    ||      (key_array - v.keys).empty? 
}
expect(incompletes).to match_array([]) 

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