Выбор элементов из хэша на основе значений подэша

#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
 

Где это использует символьные ключи и просто фильтрует массив массивов в поисках совпадающих записей, а затем возвращает их в одном (плоском) массиве.