[ruby] 루비 : 해시 키를 필터링하는 가장 쉬운 방법?

다음과 같은 해시가 있습니다.

params = { :irrelevant => "A String",
           :choice1 => "Oh look, another one",
           :choice2 => "Even more strings",
           :choice3 => "But wait",
           :irrelevant2 => "The last string" }

그리고 선택하지 않은 모든 키를 거부하는 간단한 방법이 필요합니다. choice1 또는 choice1-choice10 일 수 있습니다. 그건 다양하다.

단어 선택과 그 뒤에 숫자 또는 숫자로 키를 어떻게 구분합니까?

보너스:

탭 (\ t)을 구분 기호로 사용하여 해시를 문자열로 바꿉니다. 나는 이것을했지만 몇 줄의 코드가 필요했다. 보통 마스터 Rubicians는 한 줄 정도에 그것을 할 수 있습니다.



답변

원래 답변으로 편집 :이 답변은 (이 코멘트 시점 현재) 선택된 답변이지만,이 답변의 원래 버전은 구식입니다.

나는 다른 사람들이 내가 한 것처럼이 답변에 의해 회피되지 않도록 업데이트를 여기에 추가하고 있습니다.

다른 답변에서 언급했듯이 Ruby> = 2.5 Hash#slice는 이전에 Rails에서만 사용할 수 있었던 방법을 추가했습니다 .

예:

> { one: 1, two: 2, three: 3 }.slice(:one, :two)
=> {:one=>1, :two=>2}

편집이 끝났습니다. 다음은 Rails가없는 Ruby <2.5 인 경우 유용 할 것으로 예상되는 원래의 대답입니다.


Ruby를 사용하는 경우이 select방법을 사용할 수 있습니다 . 정규식 일치를 수행하려면 키를 Symbol에서 String으로 변환해야합니다. 그러면 선택 사항만으로 새로운 해시가 제공됩니다.

choices = params.select { |key, value| key.to_s.match(/^choice\d+/) }

또는 delete_if기존 해시를 사용 하고 수정할 수 있습니다.

params.delete_if { |key, value| !key.to_s.match(/choice\d+/) }

또는 원하는 값이 아닌 키 일 경우 다음을 수행 할 수 있습니다.

params.keys.select { |key| key.to_s.match(/^choice\d+/) }

그리고 이것은 단지 키의 배열을 줄 것입니다. [:choice1, :choice2, :choice3]


답변

Ruby에서 Hash # select는 올바른 옵션입니다. Rails를 사용한다면 Hash # slice와 Hash # slice!를 사용할 수 있습니다. 예 (레일 3.2.13)

h1 = {:a => 1, :b => 2, :c => 3, :d => 4}

h1.slice(:a, :b)         # return {:a=>1, :b=>2}, but h1 is not changed

h2 = h1.slice!(:a, :b)   # h1 = {:a=>1, :b=>2}, h2 = {:c => 3, :d => 4}


답변

가장 쉬운 방법은 gem 'activesupport'(또는 gem 'active_support') 를 포함하는 것 입니다.

그런 다음 수업 시간에

require 'active_support/core_ext/hash/slice'

그리고 전화

params.slice(:choice1, :choice2, :choice3) # => {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}

버그가있을 수있는 다른 함수를 선언하는 것은 가치가 없으며 지난 몇 년 동안 조정 된 방법을 사용하는 것이 좋습니다.


답변

가장 쉬운 방법은 gem ‘activesupport'(또는 gem ‘active_support’)를 포함하는 것입니다.

params.slice(:choice1, :choice2, :choice3)


답변

레일 작업을하고 별도의 목록에 키가있는 경우 다음 *표기법을 사용할 수 있습니다 .

keys = [:foo, :bar]
hash1 = {foo: 1, bar:2, baz: 3}
hash2 = hash1.slice(*keys)
=> {foo: 1, bar:2}

다른 답변에서 언급했듯이 slice!해시를 수정하고 지운 키 / 값을 반환하는 데 사용할 수도 있습니다 .


답변

이것은 완전한 원래 질문을 해결하는 한 줄입니다.

params.select { |k,_| k[/choice/]}.values.join('\t')

그러나 위의 대부분의 솔루션은 slice간단한 정규 표현식을 사용하여 미리 키를 알아야하는 경우를 해결합니다 .

다음은 간단하고 복잡한 유스 케이스에 작동하는 다른 접근법 입니다. 런타임시 스왑 가능

data = {}
matcher = ->(key,value) { COMPLEX LOGIC HERE }
data.select(&matcher)

이제 이것은 키 또는 값을 일치시키는 데 더 복잡한 논리를 허용 할뿐만 아니라 테스트하기도 더 쉬우 며 런타임시 일치하는 논리를 바꿀 수 있습니다.

원래 문제를 해결하기위한 Ex :

def some_method(hash, matcher)
  hash.select(&matcher).values.join('\t')
end

params = { :irrelevant => "A String",
           :choice1 => "Oh look, another one",
           :choice2 => "Even more strings",
           :choice3 => "But wait",
           :irrelevant2 => "The last string" }

some_method(params, ->(k,_) { k[/choice/]}) # => "Oh look, another one\\tEven more strings\\tBut wait"
some_method(params, ->(_,v) { v[/string/]}) # => "Even more strings\\tThe last string"


답변

Hash::select:

params = params.select { |key, value| /^choice\d+$/.match(key.to_s) }