【问题标题】:search for nonexisting item in array returns entire array在数组中搜索不存在的项返回整个数组
【发布时间】:2016-04-24 05:08:25
【问题描述】:

我正在开发一个与二维数组(从 csv 文件导入)中的第一项匹配并返回第二项的 ruby​​ 模块。这听起来非常简单,我能够让它工作,直到我尝试匹配一个不在数组中的项目。发生这种情况时,出于某种原因,将返回整个数组。我能够安装一个变通方法,涉及一个布尔变量“找到”,但我想知道为什么这不能像所写的那样工作。

require 'csv'

class Nutrition

@list = CSV.read("./lib/list.csv")


def self.carbs(name)
 grams = @list.each do |item|
    if item[0] == name
      return item[1]
    end
  end
  if grams == nil
    grams = "error"
  end
  return grams    
end
end

list.csv文件如下:

onion,13.75,0,0
carrot,11.375,0,0
cauliflower,19.375,0,0
cabbage,20.125,0,0
sw pepper,20,0,0
leek,7.5,0,0
mushroom,16.375,0,0
celery,33.25,0,0
apple,6.37,0,0
sweet potato,4.875,0,0
broccoli,14.8,0,0
red mill museli,1.52,0,0
mixed nuts,0,0,0.65
B. Sprouts,11,0,0
eggplant,16.66,0,0
quinoa,4.7,0,0
brown rice,4.33,0,0
sesame seed,0,0,4.5
sesame oil,0,0,4.655
pork chop,0,3.84,28.57
chick breast,0,3.22,27.77
lean turkey,0,4,100
ham,0,4.76,26.66

我将我的原始代码编辑如下:

require 'csv'

class Nutrition
    include Enumerable
    @list = CSV.read("./lib/list.csv")


    def self.carbs(name)
      result = @list.detect {|item| item|0| == name}
      if result.nil?
        result = "error"
      end
      result    
    end
end

现在,当我使用以下测试文件运行 rake 测试时:

require './lib/nutrition.rb'
require "test/unit"
require 'csv'


class TestNutrition < Test::Unit::TestCase
  include Enumerable
  def test_carbs()
    assert_equal(Nutrition.carbs('onion'), "13.75")
    assert_equal(Nutrition.carbs('ham'),'0')
    assert_equal(Nutrition.carbs('sawdust'), 'error')
  end

end

我最终收到以下错误消息:

语法错误,意外 == (SyntaxError) 结果 = @list.detect {|item|项目|0| == 名称} ^

但是,当我运行以下文件时,似乎一切正常,但我似乎无法通过 rake 测试:

require 'csv'

class Nutrition
    include Enumerable
    @list = CSV.read("./lib/list.csv")


    def self.carbs(name)
      result = @list.detect {|item| item[0] == name}
      if result.nil?
        result = "error"
      end
      result    
    end
end

result = Nutrition.carbs('sawdust')
puts result

【问题讨论】:

  • 你可以写@list.each { |item| return item.last if item.first == name }; "error"; end来完成这项工作,但使用find(又名detect)是可行的方法。

标签: arrays ruby csv


【解决方案1】:

您应该使用Enumerable#find 而不是对每个数组进行迭代。如果未找到该元素,则返回值为 nil。否则,您将通过 find 将其返回。

def self.carbs(name)
  grams = @list.find {|item| item[0] == name}

  if grams.nil?
    grams = "error"
  end

  grams
end

【讨论】:

  • 你可以进一步减少到def self.carbs(name); @list.find {|item| item[0] == name} || "error"; end;但对 Geo 的方法 +1。
  • 看起来应该可以,但我回来了:lib/nutrition1.rb:10: syntax error, unexpected == result = @list.find {|item|项目|0| == 名称} ^
【解决方案2】:

grams 不会是 nil,即使没有找到该项目。但是,如果达到该测试,您可以确定无论如何都没有找到该项目,因为如果它找到了某些东西,该函数将已经返回。所以到那时,你可以只返回一个错误而忘记测试grams

【讨论】:

    【解决方案3】:

    这不起作用的原因是您将grams 分配给在@list 上调用each 的返回值:grams = @list.each...。然后你在最后返回gramsreturn grams

    each 方法的返回值是原始数组,如文档中所述:http://ruby-doc.org/core-2.3.0/Array.html#method-i-each(“返回数组本身。”)

    在您的方法中,如果找到一个项目,则提前返回会阻止最后一行运行:return item[1]

    【讨论】:

      【解决方案4】:

      正确缩进代码后,很容易看出为什么总是返回整个数组:

      class Nutrition
      
          def self.carbs(name)
              grams = @list.each do |item|
                  if item[0] == name
                      return item[1]
                  end
              end
              if grams == nil
                  grams = "error"
              end
              return grams    
          end
      
      end
      

      如果name 从未匹配,那么self.carbs 执行的最后一条语句将是return grams,这当然会返回整个数组。

      编辑另外,我建议对您的设计进行一些更改。

      @ 变量是实例变量(here 的一篇很好的 SO 帖子解释了大多数 Ruby 变量之间的区别),因此在类(即静态)方法中使用 @list 可能没有意义(self.carbs)。您可以将 carbs 定义为实例方法,也可以将 list 定义为类变量 (@@list)。

      我使用前一个选项进行了一些重构,并将每个循环替换为find

      class Nutrition
          def initialize
              @list = CSV.read("./lib/list.csv")
          end
      
          def carbs(name)
              return "error" if @list.nil?
              carb = (@list.find { |x| x[0] == name })
              carb.nil? ? "error" : carb[1]
          end
      end
      

      使用后一个选项:

      class Nutrition
          @@list = CSV.read("./lib/list.csv")
      
          def self.carbs(name)
              return "error" if @list.nil?
              carb = (@@list.find { |x| x[0] == name })
              carb.nil? ? "error" : carb[1]
          end
      end
      

      【讨论】:

      • 我不确定这是否可行。我尝试在初始化中定义列表,但我总是以一个空数组结束。我可以构建数组的唯一方法是从初始化中取出列表。
      • @AntonySerio 我看不出它为什么不能工作,但您也可以将 CSV 加载到类变量中。我用代码编辑了我的答案。
      猜你喜欢
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      • 1970-01-01
      • 2012-01-04
      相关资源
      最近更新 更多