나는이 map
중 하나 값 또는 전무로 설정을 변경한다. 그런 다음 목록에서 nil 항목을 제거하고 싶습니다. 목록을 유지할 필요는 없습니다.
이것이 내가 현재 가지고있는 것입니다 :
# A simple example function, which returns a value or nil
def transform(n)
rand > 0.5 ? n * 10 : nil }
end
items.map! { |x| transform(x) } # [1, 2, 3, 4, 5] => [10, nil, 30, 40, nil]
items.reject! { |x| x.nil? } # [10, nil, 30, 40, nil] => [10, 30, 40]
루프를 수행하고 조건부로 다음과 같은 다른 배열로 수집 할 수 있다는 것을 알고 있습니다.
new_items = []
items.each do |x|
x = transform(x)
new_items.append(x) unless x.nil?
end
items = new_items
그러나 그것은 관용적 인 것처럼 보이지 않습니다. 목록에 함수를 매핑하고 nil을 제거 / 제외하는 좋은 방법이 있습니까?
답변
루비 2.7+
지금 있습니다!
Ruby 2.7 filter_map
은이 정확한 목적을 위해 소개 하고 있습니다. 관용적이고 성능이 뛰어나며 곧 표준이 될 것으로 기대합니다.
예를 들면 다음과 같습니다.
numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]
귀하의 경우 블록이 거짓으로 평가되면 간단히 다음과 같이하십시오.
items.filter_map { |x| process_x url }
” Ruby 2.7은 Enumerable # filter_map을 추가합니다 “는이 문제에 대한 초기 접근 방식 중 일부에 대한 일부 성능 벤치 마크와 함께 주제에 대해 잘 읽었습니다.
N = 1_00_000
enum = 1.upto(1_000)
Benchmark.bmbm do |x|
x.report("select + map") { N.times { enum.select { |i| i.even? }.map{|i| i + 1} } }
x.report("map + compact") { N.times { enum.map { |i| i + 1 if i.even? }.compact } }
x.report("filter_map") { N.times { enum.filter_map { |i| i + 1 if i.even? } } }
end
# Rehearsal -------------------------------------------------
# select + map 8.569651 0.051319 8.620970 ( 8.632449)
# map + compact 7.392666 0.133964 7.526630 ( 7.538013)
# filter_map 6.923772 0.022314 6.946086 ( 6.956135)
# --------------------------------------- total: 23.093686sec
#
# user system total real
# select + map 8.550637 0.033190 8.583827 ( 8.597627)
# map + compact 7.263667 0.131180 7.394847 ( 7.405570)
# filter_map 6.761388 0.018223 6.779611 ( 6.790559)
답변
당신은 사용할 수 있습니다 compact
:
[1, nil, 3, nil, nil].compact
=> [1, 3]
사람들에게 map
블록 의 출력으로 nil을 포함하는 배열을 얻고 그 블록이 조건부로 값을 반환하려고하면 코드 냄새가 나고 논리를 다시 생각해야한다고 사람들에게 상기시키고 싶습니다 .
예를 들어, 이렇게하는 일을하는 경우 :
[1,2,3].map{ |i|
if i % 2 == 0
i
end
}
# => [nil, 2, nil]
그럼 하지마 대신, 이전에 map
, reject
당신이 원하는하지 않거나 물건 select
당신은 무엇을 하시겠습니까? :
[1,2,3].select{ |i| i % 2 == 0 }.map{ |i|
i
}
# => [2]
나는 compact
우리가 올바르게 다룰 수없는 것들을 제거하기위한 마지막 도랑 노력으로 혼란을 정리하는 것을 고려한다 . 보통 우리는 무엇이 우리에게 올 것인지 알지 못했기 때문이다. 우리는 항상 프로그램에서 어떤 종류의 데이터가 던져 지는지 알아야합니다. 예기치 않은 / 알 수없는 데이터가 잘못되었습니다. 내가 일하고있는 배열에서 nil을 볼 때마다 왜 존재하는지 파헤쳐 서 루비가 nil을 생성하는 시간을 낭비하고 배열을 통해 제거하기 위해 시간을 낭비하지 않고 배열을 생성하는 코드를 개선 할 수 있는지 확인합니다. 나중에.
'Just my $%0.2f.' % [2.to_f/100]
답변
사용해보십시오 reduce
또는 inject
.
[1, 2, 3].reduce([]) { |memo, i|
if i % 2 == 0
memo << i
end
memo
}
내가 허용 대답에 동의 우리가 안된다고 map
하고 compact
있지만 같은 이유.
나는 그 깊은 느끼는 map
다음 compact
에 해당 select
다음을 map
. 고려하십시오 : map
일대일 기능입니다. 당신을 일부 값 세트에서 매핑하고있는 경우 map
에, 당신은 원하는 입력 세트의 각 값에 대한 출력 설정 한 값입니다. 사전에해야한다면 select
아마 map
세트를 원하지 않을 것 입니다. select
나중에 (또는 compact
) 해야하는 경우 map
세트에서 원하지 않을 것 입니다. 두 경우 모두 reduce
한 번만 이동하면 전체 세트에서 두 번 반복됩니다 .
또한 영어에서는 “정수 세트를 짝수 정수 세트로 줄이기”를 시도하고 있습니다.
답변
귀하의 예에서 :
items.map! { |x| process_x url } # [1, 2, 3, 4, 5] => [1, nil, 3, nil, nil]
로 바뀌지 않고 값이 변경된 것처럼 보이지 않습니다 nil
. 이 경우 다음과 같습니다.
items.select{|x| process_x url}
충분할 것입니다.
답변
빈 문자열과 nil을 거부하는 것과 같이 거부에 대한 느슨한 기준을 원한다면 다음을 사용할 수 있습니다.
[1, nil, 3, 0, ''].reject(&:blank?)
=> [1, 3, 0]
더 나아가 0 값을 거부하거나 프로세스에 더 복잡한 논리를 적용하려면 블록을 전달하여 거부하십시오.
[1, nil, 3, 0, ''].reject do |value| value.blank? || value==0 end
=> [1, 3]
[1, nil, 3, 0, '', 1000].reject do |value| value.blank? || value==0 || value>10 end
=> [1, 3]
답변
compact
이 작업을 해결하는 가장 좋은 방법은 확실 합니다. 그러나 간단한 빼기만으로 동일한 결과를 얻을 수 있습니다.
[1, nil, 3, nil, nil] - [nil]
=> [1, 3]
답변
each_with_object
아마도 가장 깨끗한 방법입니다.
new_items = items.each_with_object([]) do |x, memo|
ret = process_x(x)
memo << ret unless ret.nil?
end
제 생각에는, each_with_object
더 나은보다 inject
/ reduce
당신이 블록의 반환 값에 대해 걱정할 필요가 없기 때문에 조건의 경우입니다.