특정 조건과 일치하는 요소 만 수정하는 배열 맵
문제
루비에서는 특정 요소가 수정되는 방식으로 배열을 매핑하는 가장 표현적인 방법은 무엇입니까? 그리고 다른 사람들은 손대지 않았다?
이것은 간단한 방법입니다.
old_a = ["a", "b", "c"] # ["a", "b", "c"]
new_a = old_a.map { |x| (x=="b" ? x+"!" : x) } # ["a", "b!", "c"]
충분하지 않은 경우 "휴가"사례를 생략하십시오.
new_a = old_a.map { |x| x+"!" if x=="b" } # [nil, "b!", nil]
내가 원하는 것은 다음과 같습니다.
new_a = old_a.map_modifying_only_elements_where (Proc.new {|x| x == "b"})
do |y|
y + "!"
end
# ["a", "b!", "c"]
루비 에서이 작업을 수행 할 수있는 좋은 방법이 있습니까 (또는 레일에는 아직 찾지 못한 편의 방법이있을 수 있습니까?)?
답장 해 주셔서 감사합니다. 당신은 단지 사용하는 것이 최선이라고 총체적으로 설득했지만 map
Ternary 운영자와 함께, 여러분 중 일부는 매우 흥미로운 답변을 게시했습니다!
해결책
지도 문이 그대로 좋다는 데 동의합니다. 명확하고 단순하며 누구나 유지하기가 쉽습니다.
더 복잡한 것을 원한다면 이건 어때?
module Enumerable
def enum_filter(&filter)
FilteredEnumerator.new(self, &filter)
end
alias :on :enum_filter
class FilteredEnumerator
include Enumerable
def initialize(enum, &filter)
@enum, @filter = enum, filter
if enum.respond_to?(:map!)
def self.map!
@enum.map! { |elt| @filter[elt] ? yield(elt) : elt }
end
end
end
def each
@enum.each { |elt| yield(elt) if @filter[elt] }
end
def each_with_index
@enum.each_with_index { |elt,index| yield(elt, index) if @filter[elt] }
end
def map
@enum.map { |elt| @filter[elt] ? yield(elt) : elt }
end
alias :and :enum_filter
def or
FilteredEnumerator.new(@enum) { |elt| @filter[elt] || yield(elt) }
end
end
end
%w{ a b c }.on { |x| x == 'b' }.map { |x| x + "!" } #=> [ 'a', 'b!', 'c' ]
require 'set'
Set.new(%w{ He likes dogs}).on { |x| x.length % 2 == 0 }.map! { |x| x.reverse } #=> #<Set: {"likes", "eH", "sgod"}>
('a'..'z').on { |x| x[0] % 6 == 0 }.or { |x| 'aeiouy'[x] }.to_a.join #=> "aefiloruxy"
다른 팁
배열은 포인터이기 때문에 다음에도 효과가 있습니다.
a = ["hello", "to", "you", "dude"]
a.select {|i| i.length <= 3 }.each {|i| i << "!" }
puts a.inspect
# => ["hello", "to!", "you!", "dude"]
루프에서 새 개체를 만드는 대신 개체를 변경하는 메소드를 사용해야합니다. 예를 들어 upcase!
비교 upcase
.
정확한 절차는 정확히 달성하려는 것에 달려 있습니다. Foo-Bar 예제로 명확한 답을 못 박는 것은 어렵습니다.
old_a.map! { |a| a == "b" ? a + "!" : a }
주어진
=> ["a", "b!", "c"]
map!
수신기를 제자리에 수정합니다 old_a
이제 반환 된 배열입니다.
당신의 map
솔루션이 가장 좋습니다. 왜 당신이 MAP_MODIFYING_ONLY_ELEMENTS_가 어떻게 든 더 낫다고 생각하는지 잘 모르겠습니다. 사용 map
더 깨끗하고 간결하며 여러 블록이 필요하지 않습니다.
짧막 한 농담:
["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative }
위의 코드에서 [] "누적"으로 시작합니다. 열거 자 (우리의 경우 배열, [ "a", "b", "c"]를 통해 열거 할 때, 누적 및 "현재"항목은 우리 블록 (| 누적, i |)에 전달됩니다. 블록의 실행 결과는 누적에 할당됩니다. 위에서하는 일은 항목이 "B"가 아니고 "B!"를 부여 할 때 누적을 변경하지 않는 것입니다. 누적 배열을 위해 배열이 필요할 때 반환합니다. b.
위에 사용되는 답이 있습니다 select
, 이것이 가장 쉬운 방법입니다.
당신은 결합 할 수 있습니다 select
~와 함께 map
찾고있는 것을 달성하기 위해 :
arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" }
=> ["b!"]
내부 select
블록, 요소가 "선택"할 조건을 지정합니다. 배열을 반환합니다. 결과 배열에서 "맵"을 호출하여 느낌표를 추가 할 수 있습니다.
이전 배열이 필요하지 않으면지도를 선호합니다! 이 경우 사용할 수 있기 때문에! 당신을 나타내는 메소드 배열을 제자리에 변경하고 있습니다.
self.answers.map!{ |x| (x=="b" ? x+"!" : x) }
나는 이것을 선호한다 :
new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) }
몇 줄 길이이지만 여기에 대한 대안이 있습니다.
oa = %w| a b c |
na = oa.partition { |a| a == 'b' }
na.first.collect! { |a| a+'!' }
na.flatten! #Add .sort! here if you wish
p na
# >> ["b!", "a", "c"]
Ternary와의 수집은 제 생각에 가장 잘 보입니다.
이것을 달성하는 가장 좋은 방법은 사용하는 것입니다. tap
arr = [1,2,3,4,5,6]
[].tap do |a|
arr.each { |x| a << x if x%2==0 }
end