我正在尝试访问数组中对象的属性,但我一直收到错误,说数组值为nil:
@notifications.each do |note|
@users << User.find(note.notifier_id)
end
@unreads = []
for i in 0..@users.count
@unreads[i] = 0
@current_user.notifications.each do |n|
if n.notifier_id == @users[i].id && n.seen == false
@unreads[i] += 1
end
end
end
我收到错误:NoMethodError (undefined method 'id' for nil:NilClass):
,来自@users[i].id
如果我没有运行for循环并打印出@users
的值,我会得到预期的输出,其中包含有效的id
值。
如何从阵列中正确访问User
对象?
答案 0 :(得分:3)
您的代码中存在两个问题。
第一个问题永远是个问题。正如@sawa回答的那样,您在for
循环中使用的范围内有一个一个一个错误。使用三个点:
for i in 0...@users.count
而不是两个点:
for i in 0..@users.count
Range
类的Ruby 2.3.0 documentation读取:
使用
..
构建的范围包括从开始到结束。使用...
创建的内容排除了结束值。
nil
结果 User.find(note.notifier_id)
可能会返回nil
。与问题#1不同,这可能并不总是发生。我不是假设你在下面的建议中使用Rails,因为你没有提到使用它。
解决此问题的一种方法是确保User.find
始终返回响应#id
的对象。在大多数情况下,这将是User
的实例,当查询无法找到User
记录时,可能是空对象。您可以看到空对象模式here的示例。
解决此问题的另一种方法是在向@users[i]
消息发送之前检查#id
是否存在。
@notifications.each do |note|
@users << User.find(note.notifier_id)
end
@unreads = []
for i in 0...@users.count
@unreads[i] = 0
@current_user.notifications.each do |n|
if @users[i] && n.notifier_id == @users[i].id && n.seen == false
@unreads[i] += 1
end
end
end
如果@users[i]
为nil
,则代码将不会执行@users[i].id
。然而,将额外检查添加到条件会使代码更难阅读。
在计算未读项目数之前,您还可以使用#compact
从nil
数组中丢弃@users
个值。我在下面使用each_with_index
,因为它比for
更加惯用。
@notifications.each do |note|
@users << User.find(note.notifier_id)
end
@unreads = []
@users.compact.each_with_index do |user, i|
@unreads[i] = 0
@current_user.notifications.each do |n|
if n.notifier_id == @users[i].id && n.seen == false
@unreads[i] += 1
end
end
end
答案 1 :(得分:2)
您的访问方式是正确的,但您尝试访问不存在的索引@users
。尝试:
for i in 0...@users.count
答案 2 :(得分:0)
0..@users.count
的有@user.count + 1
个元素,但您可以通过0..(@users.count-1)
的索引进行访问。
最好使用each_with_index
循环@users
@users.each_with_index do |user, i|
@unreads[i] = @current_user.
notifications.
where(notifier_id: user.id, seen: false).
count
end
更短的版本
@users = User.where(id: @notifications.map(&:notifier_id))
@unreads = @users.map { |user| @current_user.notifications.where(notifier_id: user.id, seen: false).count }