得到数组对象 - get object from array

- 此内容更新于:2016-02-02
主题:

我想访问数组中的对象的属性,但我得到一个错误说数组值为nil:我得到错误:来自如果我不跑,for循环打印出的价值,得到期望的输出,其中包括有效值。我如何正确地访问一个对象从一个数组?

原文:

I am trying to access the property of an object in an array, but I keep getting an error saying that the array value is 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

I get the error: NoMethodError (undefined method 'id' for nil:NilClass):, coming from @users[i].id

If I don't run that for loop and just print out the value of @users, I get the expected output, which includes valid id values.

How do I properly access a User object from an array?

网友:岂不更好,让您的数据库呢?

(原文:Wouldn't it be better to let your database do this?)

楼主:你的意思如何?指数见过吗?

(原文:how do you mean? Index on seen?)

网友:你可以做所有这一切与ActiveRecord查询。

(原文:You can do all of this with ActiveRecord queries.)

网友:我认为@sawa可能解决你的问题。解决方案不会防止如果回报,虽然。

(原文:I think @sawa probably solved your problem. That solution will not prevent a NoMethodError if User.find ever returns nil, though.)

楼主:@Jordan谢谢,我马上去

(原文:@Jordan thanks, I'll look into that)

解决方案:
你的访问方式是正确的,但你尝试访问索引不存在。试一试:
原文:

Your way of accessing is correct, but you are trying to access @users at an index that does not exist. Try:

for i in 0...@users.count
楼主:那不是我有什么吗?

(原文:isn't that exactly what I have?)

网友:三个点和两个。三个点不包括最后的数字,两个点包括它。在ruby是零索引数组,所以10项数组索引0到9,不是0到10。

(原文:Three dots vs two. Three dots excludes the final number, two dots includes it. Arrays in ruby are zero indexed, so an array of 10 items would have indexes 0 through 9, not 0 through 10.)

解决方案:
代码中有两个问题。1。错误总是会第一个问题一个问题。@sawa回答,你有一个错误在你使用你的循环。使用三个点:而不是两个点:Rubytripwire文档类写道:范围构造使用运行在内地从开始到结束。那些使用排除创建最终的价值。2。可能导致查询可能会返回零。与#1问题,这并不总是发生。我不假设你使用Rails在下面我的建议,因为你没有提到使用它。为了解决这一问题的方法之一是确保总是返回一个响应对象。这将是一个实例,在大多数情况下,甚至一个空对象查询找不到一个用户记录。你可以看到一个空对象模式的例子。另一种方式来处理这个问题是在发送之前检查是否存在#id的消息。如果@users[我]是零,代码不会执行。添加额外的检查条件使代码难以阅读,然而。你也可以丢弃nil值数组与之前计算未读条目的数量。我使用下面,因为它有点更地道。
原文:

There are two problems in your code.

1. Off-by-one Error

The first issue will always be a problem. As @sawa answered, you have an off-by-one error in the range you're using in your for loop. Use three dots:

for i in 0...@users.count

instead of two dots:

for i in 0..@users.count

The Ruby 2.3.0 documentation for the Range class reads:

Ranges constructed using .. run from the beginning to the end inclusively. Those created using ... exclude the end value.

2. Potential nil Result in Query

It's possible that User.find(note.notifier_id) will return nil. Unlike problem #1, this may not always happen. I'm not assuming you're using Rails in my suggestions below, since you didn't mention using it.

One way to deal with this is to ensure that User.find always returns an object that responds to #id. This would be an instance of User in most cases, and possibly a null object when the query can't find a User record. You can see an example of the null object pattern here.

Another way to deal with this is to check to see if @users[i] is present before sending it the #id message.

@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

If @users[i] is nil, the code won't ever execute @users[i].id. Adding the extra check to the conditional makes the code harder to read, however.

You could also discard nil values from your @users array with #compact before counting the number of unread items. I'm using each_with_index below, since it's a bit more idiomatic than 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
楼主:谢谢你指出第二个问题。它不会是一个问题现在因为用户不能被删除,但是帐号删除最终会实现的,所以这是一个等待发生的错误。

(原文:Thanks for pointing out the second issue. It wouldn't be a problem now since Users cannot be deleted, but account deletion will eventually be implemented, so that was a bug waiting to happen.)

解决方案:
从有元素,但是你可以通过索引访问。最好是使用循环在@users短版本
原文:

from 0..@users.count, there are @user.count + 1 elements, but you can access by index from 0..(@users.count-1). it is better for using each_with_index to loop in @users

@users.each_with_index do |user, i|
  @unreads[i] = @current_user.
    notifications.
    where(notifier_id: user.id, seen: false).
    count
end

Shorter version

@users = User.where(id: @notifications.map(&:notifier_id)) 
@unreads = @users.map { |user| @current_user.notifications.where(notifier_id: user.id, seen: false).count }