Ruby:在数组中查找与条件匹配的项,但如果没有一个元素与条件匹配,则会引发错误

Ruby: Find the item in an array that matches a condition, but raise error if there is not exactly one element that matches the condition

提问人:Andy 提问时间:3/10/2023 最后编辑:Andy 更新时间:3/14/2023 访问量:144

问:

我发现自己最近实施了几次的模式是这样的:

def request_from_transaction(array_of_messages) 
  request_array = array_of_messages.select { |msg| msg.is_request? } 
  raise MyExceptions::NotOneRequestError('The system expects only 1 request') unless request_array.size == 1

  request_array[0]
end

因为我最近遇到过几次这种情况,我想知道这是否是一种常见的模式,如果是的话,是否有一种惯用的、优雅的方式可以在 ruby 中对这种行为进行编码,而无需有条件和明确的提高。

最好只在一行代码中使用 ruby 标准库。

我知道:

array_of_messages.find { |msg| msg.is_request? } 

但这将返回数组中与条件匹配的第一条消息,如果数组中实际上有 2 个请求,这将静默失败。工作假设是数组中只有一个匹配的项目,所以我想知道这个假设是否不正确。

我希望存在类似的东西:

array_of_messages.find_one_or_raise { |msg| msg.is_request? } 

据我所知,这在标准库中不存在。

这种方法的期望行为是:

[request, response, update, update].find_one_or_raise { |msg| msg.is_request? } 
# => request

[request, response, request, update].find_one_or_raise { |msg| msg.is_request? } 
# => exception raised 'found more than one item that matched'

[response, update, update].find_one_or_raise { |msg| msg.is_request? } 
# => exception raised 'found none that matched'

我想我可以自己实现这个,并在数组上修补一个新方法,如果我稍微考虑一下,甚至可以枚举,但理想情况下,我希望已经有一些东西了。

当然,我可以继续做我正在做的事情,并重复同样的模式......

感谢您的任何建议。

数组 Ruby 验证

评论

0赞 spickermann 3/10/2023
当您只期望列表中有一个具有特定条件的元素时,那么我想知道而不是数组是否是更好的数据结构?Set
0赞 Andy 3/10/2023
感谢您的评论@spickermann,这是一个有趣的想法,可以有几个更新,但它们将是不同的更新,所以我认为一套可以在这里工作。
0赞 Andy 3/10/2023
作为模式,创建对象(消息数组)可能是进行任何所需检查的最佳位置,而不是等到我需要请求时才发现有多个请求。
0赞 max 3/11/2023
这里让我印象深刻的一件事是.这将引发一个一般错误,这使得在不对要挽救的内容不宽容的情况下挽救异常变得非常困难,并且这也使调试更加困难。更具体。raise('The system expects only 1 request')
0赞 Andy 3/14/2023
非常好的点@max。我试图使代码示例尽可能简单,但绝对,提出一个特定的异常类是正确的做法,有人在实际项目中实现它。如果我能弄清楚如何做,我会编辑这个问题。

答:

1赞 Siim Liiser 3/10/2023 #1

Rails 7 引入了 Enumerable#sole which kind of do what you want。不过,它不接受块,因此您仍然必须单独进行选择。它确实很好地封装了错误引发,因此可能仍然有用

array_of_messages.select(&:is_request?).sole

如果没有 Rails I,您拥有的解决方案几乎是您能得到的最好的解决方案。可以稍微优化一下,一旦找到第二个元素就停止迭代。

评论

0赞 Andy 3/10/2023
谢谢,这非常接近,我没有意识到。我想我的项目中已经有 ActiveSupport,尽管它不是 RoR 项目。正如你所说,这样做确实会删除所有条件和显式加薪。