[ruby] Ruby에는 ‘select’와 ‘map’을 결합한 Array 메서드가 있습니까?

문자열 값을 포함하는 Ruby 배열이 있습니다. 다음을 수행해야합니다.

  1. 일부 술어와 일치하는 모든 요소 찾기
  2. 변환을 통해 일치하는 요소 실행
  3. 결과를 배열로 반환

지금 내 솔루션은 다음과 같습니다.

def example
  matchingLines = @lines.select{ |line| ... }
  results = matchingLines.map{ |line| ... }
  return results.uniq.sort
end

선택 및 매핑을 단일 논리 문으로 결합하는 Array 또는 Enumerable 메서드가 있습니까?



답변

나는 보통 mapcompact내 선택 기준과 함께 접미사로 사용 if합니다. compactnil을 제거합니다.

jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}
 => [3, 3, 3, nil, nil, nil]


jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}.compact
 => [3, 3, 3] 


답변

reduce이를 위해 사용할 수 있으며 한 번만 패스하면됩니다.

[1,1,1,2,3,4].reduce([]) { |a, n| a.push(n*3) if n==1; a }
=> [3, 3, 3] 

즉, 상태를 원하는대로 초기화하십시오 (이 경우 채울 빈 목록 : [] , 항상 원래 목록의 각 요소에 대한 수정 사항과 함께이 값을 반환해야합니다 (이 경우 수정 된 요소 목록에 푸시 됨).

한 번의 패스 ( map+ select또는compact 두 번의 패스가 필요함).

귀하의 경우 :

def example
  results = @lines.reduce([]) do |lines, line|
    lines.push( ...(line) ) if ...
    lines
  end
  return results.uniq.sort
end


답변

Ruby 2.7 이상

이제 있습니다!

filter_map이 정확한 목적을 위해 Ruby 2.7이 도입 되었습니다. 관용적이고 성능이 뛰어나며 곧 표준이 될 것으로 기대합니다.

예를 들면 :

numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]

여기 에 주제에 대한 좋은 읽기가 있습니다.

누군가에게 유용하기를 바랍니다!


답변

이것에 접근하는 또 다른 다른 방법은 new (이 질문과 관련하여)를 사용하는 것입니다 Enumerator::Lazy.

def example
  @lines.lazy
        .select { |line| line.property == requirement }
        .map    { |line| transforming_method(line) }
        .uniq
        .sort
end

.lazy메서드는 lazy 열거자를 반환합니다. 호출 .select또는 .map게으른 열거에 또 다른 게으른 열거를 돌려줍니다. 한 번만 호출 .uniq하면 실제로 열거자를 강제 적용하고 배열을 반환합니다. 따라서 효과적으로 발생하는 것은 귀하 .select.map통화가 하나로 결합되는 것입니다. @lines한 번만 반복 .select하여.map .

내 본능은 Adam의 reduce방법이 조금 더 빠를 것이지만 이것이 훨씬 더 읽기 쉽다고 생각합니다.


이것의 주요 결과는 각 후속 메서드 호출에 대해 중간 배열 개체가 생성되지 않는다는 것입니다. 정상적인 @lines.select.map상황 에서는에 select의해 수정 된 배열을 반환하고 map다시 배열을 반환합니다. 이에 비해 지연 평가는 배열을 한 번만 생성합니다. 이것은 초기 컬렉션 개체가 클 때 유용합니다. 또한 무한 열거 자로 작업 할 수 있습니다 random_number_generator.lazy.select(&:odd?).take(10).


답변

당신이있는 경우 select사용 할 수있는 case연산자를 ( ===) grep좋은 대안이다 :

p [1,2,'not_a_number',3].grep(Integer){|x| -x } #=> [-1, -2, -3]

p ['1','2','not_a_number','3'].grep(/\D/, &:upcase) #=> ["NOT_A_NUMBER"]

더 복잡한 논리가 필요한 경우 람다를 만들 수 있습니다.

my_favourite_numbers = [1,4,6]

is_a_favourite_number = -> x { my_favourite_numbers.include? x }

make_awesome = -> x { "***#{x}***" }

my_data = [1,2,3,4]

p my_data.grep(is_a_favourite_number, &make_awesome) #=> ["***1***", "***4***"]


답변

하나가 있는지 잘 모르겠습니다. 및 을 추가 하는 Enumerable 모듈 은 하나를 표시하지 않습니다.selectmap

두 블록을 select_and_transform메서드 에 전달해야하는데 , 이는 약간 직관적이지 않은 IMHO입니다.

분명히, 더 읽기 쉽게 함께 연결할 수 있습니다.

transformed_list = lines.select{|line| ...}.map{|line| ... }


답변

간단한 대답 :

당신은 n 개의 기록을 가지고 있고, 당신이 원하는 경우 selectmap다음 조건에 따라

records.map { |record| record.attribute if condition }.compact

여기에서 속성은 레코드에서 원하는 모든 항목과 조건을 확인할 수 있습니다.

compact는 if 조건에서 나온 불필요한 nil을 플러시하는 것입니다.