¿Alguien puede arrojar algo de luz sobre por qué esto se está comportando así? Espero que la colección omita los resultados nulos, y solo recopile aquellos que califican bajo la condición de include? . Entradas de primer y tercer resultado, espero no estar allí. Por alguna razón, de hecho están incluidos. ¿Qué estoy haciendo mal?
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."]Este es el comportamiento esperado: el map transforma una matriz uno a uno y cualquier declaración en forma:
do_sth if some_condition devuelve nil cuando alguna condición es falsa y nil es solo un objeto como cualquier otra cosa.
La forma estándar de lidiar con esto sería usar select before call to map o llamar compact after map .
Desde Ruby 2.7 tenemos otro método que combina los dos: filter_map que ignora los valores falsos devueltos:
conditions = f.filter_map { |i| i['title'] if i['title'].include?('night') } ==> ["Tuesday night: Cloudy. High plus 2.", "Tuesday night: Cloudy. High plus 2."]Porque en cada iteración, recopilar/mapa devuelve algo (también nulo en este caso)...
Puede usar select para filtrar su matriz. Algo como esto:
f.select { |i| i['title'].include?('night') }.collect {|i| i['title']}#map transforma una colección en otra colección del mismo tamaño. Si el bloque devuelve valores nil , esos valores nil terminarán en la nueva colección.
Una declaración como i['title'] if i['title'].include?('night') devolverá nil en el caso de que el título no contenga night .
Puede usar #filter_map , #select + #map , #inject o #each_with_object
Ejecuté un micro-benchmark rápido y, sorprendentemente, iterar la colección dos veces (usando #select AND #map ) no es más lento.
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