#ruby
#ruby
Вопрос:
У меня есть следующий вывод JSON из API:
{
"Objects": [
{
"FieldValues": [
{
"Field": {
"Name": "Nuix Field"
},
"Value": "Primary Date"
},
{
"Field": {
"Name": "Field Type"
},
"Value": {
"Name": "Nuix"
}
},
{
"Field": {
"Name": "Field Category"
},
"Value": {
"Name": "Generic"
}
}
]
}
]
}
Я хочу иметь возможность выбирать все Objects
, где «Поле» имеет «Имя» типа «Поля», а его «Значение» имеет «Имя» типа «Nuix».
Это моя попытка, но я чувствую, что есть лучший способ сделать это?
json = JSON.parse(response)
results = []
json["Objects"].each do |obj|
obj["FieldValues"].each do |fv|
if fv["Field"]["Name"] == "Field Type" amp;amp; fv["Value"]["Name"] == "Nuix"
results << obj
end
end
end
Комментарии:
1. Поскольку
FieldValues
иObjects
являются массивом, с точки зрения выполнения особой разницы не будет, вам все равно нужно перебирать все объекты значений полей. Что вы подразумеваете под «лучшим способом»? Какие улучшения вы ищете?2. В вашей структуре есть как хэш, так
"Name"
и иногда не хэш, а строка?3. Первая запись имеет
"Value": "Primary Date"
, а другие имеют"Value": { "Name": ... }
.4. Ваш хэш имеет одну пару ключ-значение, ключом является
:Objects
значение, представляющее собой массив, содержащий одно значение, хэш, содержащий одну пару ключ-значение, ключ:FieldValues
, являющийся значением, являющимся массивом хэшей. Еслиh
это ваш хэш, пустьarr = h[:Objects][0][:FieldValues]
. Вы хотите вернуть массив, содержащий те элементыg
arr
(хэши), для которыхg[:Field][:Name]
равно"Field Type"
иg[:Value][:Name]
равно"Nuix"
?5. Есть ли более «рубиновый» способ сделать это, то есть однострочный? Я родом из C #, поэтому обычно я мог бы использовать LINQ в подобных ситуациях, чтобы получить аккуратное однострочное решение. @tadman — да, это правильно, именно так API возвращает результаты. Я удалил часть другого содержимого в хэше, но именно так оно и было возвращено.
Ответ №1:
Один из вариантов — не перебирать все FieldValues
, а только до тех пор, пока ожидаемый не будет найден с any?
помощью метода.
Затем вы можете упростить код с select
помощью метода, который создаст новый массив только с «удовлетворенными» объектами.
objects_with_required_fields = json.fetch("Objects", []).select do |obj|
obj.fetch("FieldValues", []).any? do |fv|
name = fv.dig("Field", "Name")
value = fv["Value"]
name == "Field Type" amp;amp; value.is_a?(Hash) amp;amp; value["Name"] == "Nuix"
end
end
Ответ №2:
Вот более минимальное решение Ruby:
json = JSON.parse(response, symbolize_names: true)
target = [ 'Field Type', 'Value' ]
# For each of the entries in Objects...
results = json[:Objects].flat_map do |obj|
# ...filter out those that...
obj[:FieldValues].select do |fv|
# ...match the target criteria.
[ fv.dig(:Field, :Name), fv[:Value] ] == target
end
end
Где это использует символьные ключи и просто фильтрует массив массивов в поисках совпадающих записей, а затем возвращает их в одном (плоском) массиве.