【问题标题】:Conditional in Ruby Array.collect Iterator BlockRuby Array.collect 迭代器块中的条件
【发布时间】:2021-12-05 05:46:10
【问题描述】:

任何人都可以阐明为什么会这样吗?我希望该集合省略 nil 结果,并且仅收集符合include? 条件的结果。第一个和第三个结果条目,我希望不会出现。出于某种原因,它们确实包括在内。我做错了什么?

f = [
{"title"=>"Tuesday: Cloudy. High plus 12.",
"link"=>{"type"=>"text/html",
  "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}},

{"title"=>"Tuesday night: Cloudy. High plus 2.",
"link"=>{"type"=>"text/html",
  "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}},

{"title"=>"Wednesday: Cloudy. High plus 10.",
"link"=>{"type"=>"text/html",
  "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}},

{"title"=>"Tuesday night: Cloudy. High plus 2.",
"link"=>{"type"=>"text/html",
  "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}}
]

conditions = f.collect { |i| i['title'] if i['title'].include?('night') }
==> [nil, "Tuesday night: Cloudy. High plus 2.", nil, "Tuesday night: Cloudy. High plus 2."]

【问题讨论】:

    标签: arrays ruby iterator block collect


    【解决方案1】:

    这是预期的行为 - map 将一个数组一对一地转换为任何形式的语句:

    do_sth if some_condition
    

    当某些条件不成立并且nil 只是一个对象时返回nil

    处理此问题的标准方法是在调用 map 之前使用 select 或在 map 之后调用 compact

    从 ruby​​ 2.7 开始,我们有另一种方法将两者结合起来:filter_map 忽略返回的虚假值:

    conditions = f.filter_map { |i| i['title'] if i['title'].include?('night') }
    ==> ["Tuesday night: Cloudy. High plus 2.", "Tuesday night: Cloudy. High plus 2."]
    

    【讨论】:

    • 可能是他们添加该方法的原因,因为人们正在应用条件并获得nils
    【解决方案2】:

    因为在每次迭代中,collect/map 都会返回一些东西(在这种情况下也是 nil)...

    您可以使用 select 来过滤您的数组。像这样的:

    f.select { |i| i['title'].include?('night') }.collect {|i| i['title']}
    

    【讨论】:

    • 是的,我原以为 nils 不会出现。我想它展示了一个迭代。我现在选择了Array.compact 来删除nil 条目。
    【解决方案3】:

    #map 将一个集合转换为另一个相同大小的集合。如果块返回 nil 值,那么这些 nils 将最终出现在新集合中。

    如果标题不包含nighti['title'] if i['title'].include?('night') 之类的语句将返回 nil

    您可以使用#filter_map#select + #map#inject#each_with_object

    我运行了一个快速的微基准测试并出人意料地迭代了两次集合(使用#select AND #map)并没有变慢。

    items = [
    {"title"=>"Tuesday: Cloudy. High plus 12.",
    "link"=>{"type"=>"text/html",
      "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}},
    
    {"title"=>"Tuesday night: Cloudy. High plus 2.",
    "link"=>{"type"=>"text/html",
      "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}},
    
    {"title"=>"Wednesday: Cloudy. High plus 10.",
    "link"=>{"type"=>"text/html",
      "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}},
    
    {"title"=>"Tuesday night: Cloudy. High plus 2.",
    "link"=>{"type"=>"text/html",
      "href"=>"https://www.weather.gc.ca/city/pages/mb-42_metric_e.html"}}
    ]
    require "benchmark/ips"
    Benchmark.ips do |x|
      x.compare!
      x.report("filter_map") do
        items.filter_map { |item| item["title"] if item["title"].include?("night")}
      end
      x.report("select_map") do
        items.select { |item| item["title"].include?("night") }.map { |item| item["title"] }
      end
      x.report("each_with_object") do
        items.each_with_object([]) { |item, accu| accu << item["title"] if item["title"].include?("night"); nil }
      end
      x.report("inject") do
        items.inject([]) { |accu, item| accu << item["title"] if item["title"].include?("night"); accu }
      end
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      • 2011-02-14
      • 2014-07-19
      • 2012-02-17
      • 2011-10-22
      相关资源
      最近更新 更多