#ruby-on-rails
#ruby-on-rails
Вопрос:
У меня есть этот массив:
[{:student=>{:id=>1, :firstName=>"firstName", :lastName=>"lastName",:age=>9}, :pretest=>nil,
:diagnostic=>nil, :result=>nil, :completedAt=>nil},
{:student=>{:id=>2
:firstName=>"John", :lastName=>"Doe",:age=>9}, :pretest=>{:id=>22, :score=>{:attempted=>15,
:correct=>13, :total=>15}, :title=>"Test", :age=>6, :diagnostic=>{:id=>23, :score=>{:attempted=>40, :correct=>17,
:total=>40}, result: "OK", :completedAt=>Thu, 01 Oct 2020 16:09:34 UTC 00:00}]
Я пытаюсь получить доступ к заголовку предварительного теста, когда он существует. Я делаю
student_data = report[:entries]
student_data.each { |x| puts x[:pretest][:title]}
но я получаю неопределенный метод []
для ошибки nil: NilClass .
Когда я делаю
student_data = report[:entries]
student_data.each { |x| puts x[:pretest]}
Я вижу {:id=>22, :score=>{:attempted=>15, :correct=>13, :total=>15}, :title=>"Test", :age=>6}
.
Итак, почему student_data.each { |x| puts x[:pretest]}
работает, но student_data.each { |x| puts x[:pretest][:title]}
выдает ошибку?
Ответ №1:
Возникает ошибка, поскольку первый хэш в массиве имеет :pretest=>nil
.
В Ruby 2.3 введен хэш #dig, который можно использовать для безопасной навигации по хэшам:
student_data.each { |x| puts x.dig(:pretest, :title) }
В более старых версиях ruby существуют различные неуклюжие способы безопасной навигации по хэшу:
hash = {}
# Using fetch with a default value
hash.fetch(:foo, {})[:bar]
# Tail condition
hash[:foo][:bar] if hash[:foo]
# Or with try? from ActiveSupport
hash[:pretest].try?(:[], :foo)
Ответ №2:
Глядя на ваш код, вы правы, предварительный тест существует для одного из учеников, но не для всех:
[{:student=>{:id=>1, :firstName=>"firstName", :lastName=>"lastName",:age=>9}, :pretest=>nil,
:diagnostic=>nil, :result=>nil, :completedAt=>nil},
{:student=>{:id=>2
:firstName=>"John", :lastName=>"Doe",:age=>9}, :pretest=>{:id=>22, :score=>{:attempted=>15,
:correct=>13, :total=>15}, :title=>"Test", :age=>6, :diagnostic=>{:id=>23, :score=>{:attempted=>40, :correct=>17,
:total=>40}, result: "OK", :completedAt=>Thu, 01 Oct 2020 16:09:34 UTC 00:00}]
Очевидно, что для идентификатора студента = 1 предварительное тестирование равно нулю. Таким образом, попытка перебрать учеников завершится неудачей, поскольку она прервется при первом ученике. Вы можете избежать этого, сказав ruby «попробовать» этот хэш-ключ и вернуть значение по умолчанию, если nil . Вы можете использовать dig или попробовать:
student_data.each { |x| puts x.dig(:pretest, :title) }
Или более уродливая версия (но облегчает понимание IMO)
student_data.each { |x| puts x[:pretest].try(:[],:title) }
Комментарии:
1. Привет, нет, я не создал первого ученика по ошибке. Учащийся будет иметь нулевое значение, если он не завершит свой тест. В моем случае у меня есть два ученика, 1 и 2, и я хочу увидеть название тестов тех учеников, у которых есть завершенный тест.
2. Я обновил ответ на то, что может вам помочь
Ответ №3:
На самом деле я попробовал что-то другое, что также работало, в основном переходя к следующей записи, если предыдущая была нулевой:
def print_existing_titles(student_data)
student_data.any? do |data|
next if data[:pretest].nil?
puts data[:pretest][:title]
end