提问人:Andy 提问时间:3/10/2023 最后编辑:Andy 更新时间:3/14/2023 访问量:144
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
问:
我发现自己最近实施了几次的模式是这样的:
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'
我想我可以自己实现这个,并在数组上修补一个新方法,如果我稍微考虑一下,甚至可以枚举,但理想情况下,我希望已经有一些东西了。
当然,我可以继续做我正在做的事情,并重复同样的模式......
感谢您的任何建议。
答:
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 项目。正如你所说,这样做确实会删除所有条件和显式加薪。
评论
Set
raise('The system expects only 1 request')