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


225

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

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는 한 줄 정도에 그것을 할 수 있습니다.


보너스 질문의 관점에서 문자열의 모양을 명확히 할 수 있습니까?
mikej

물론, 위의 예 해시는 얻을 것이다 : "아의 모습을, 다른 하나 \ tEven 더 문자열 \ TBUT 기다려야"(오직 그들 사이에, 문자열의 끝에서하지 \ t로)
데릭

주석에서 예제 문자열이 잘 렸습니다. 편집 링크를 사용하여 질문을 편집하고 거기에 예제를 추가 할 수 있습니다. 달성하려는 루비를 게시 할 수 있습니다.
mikej

2
의 중복 가능성 루비 해시 필터
jbochi

답변:


281

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

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

다른 답변에서 언급했듯이 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]


1
우수한! 일단 보면, 이것은 매우 간단합니다! 대단히 감사합니다. 시간을내어 답변을 주신 다른 분들께도 감사드립니다.
데릭

2
요즘 IIRC에서는 정규식으로 기호를 구문 분석 할 수 있습니다.
앤드류 그림

@ 앤드류, 당신이 옳다고 생각합니다. 이 질문에 대한 답을 테스트 할 때 1.8.7에있었습니다.
mikej

1
에 대한 대응은 .select입니다 .reject그 코드를 더욱 관용적 만드는 경우.
Joe Atzberger

#slice방법도 activesupport없이 2.5.3에 나를 위해 작동합니다.
graywolf

305

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}

12
단지 반환 {B는 => 2 'A'=> 1} = 캐릭터 keys..h1 조심 {B => 2} h1.slice와 (A, : b)
davidcollom

멋진 해결책. rspec에서 결과를 반환하는 데 이것을 사용하여 해시 내에서 해시 값을 구문 분석하려고했습니다. ```test = {: a => 1, : b => 2, c => {: A => 1, : B => 2}} solution ==> test.c.slice (: B)`` `
PriyankaK

ActiveSupport로 Rails를 교체하면 완벽합니다.)
Geoffroy

5
"choice + int"인 키의 값을 추출하는 방법을 원하기 때문에 질문에 대답하지 않습니다. 솔루션은 미리 알려진 키만 추출 할 수 있습니다.
metakungfu

46

가장 쉬운 방법은 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"}

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


이를 위해서는 최소한 2.5에 대한 액티브 레코드가 필요하지 않습니다.
graywolf

13

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

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


4
답변에 자세한 내용을 추가하십시오.
Mohit Jain

13

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

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

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


9

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

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"

1
나는 정말 매처를 좋아합니다
Calin


6

나머지 해시를 원할 경우 :

params.delete_if {|k, v| ! k.match(/choice[0-9]+/)}

또는 키를 원할 경우 :

params.keys.delete_if {|k| ! k.match(/choice[0-9]+/)}

choiceX와 unrelevantX의 수에 따라 OR delete_if를 선택하는 것이 좋습니다. 더 많은 선택권이 있으면 delete_if가 더 좋습니다.
ayckoster

6

이것을 이니셜 라이저에 넣으십시오.

class Hash
  def filter(*args)
    return nil if args.try(:empty?)
    if args.size == 1
      args[0] = args[0].to_s if args[0].is_a?(Symbol)
      self.select {|key| key.to_s.match(args.first) }
    else
      self.select {|key| args.include?(key)}
    end
  end
end

그럼 넌 할 수있어

{a: "1", b: "b", c: "c", d: "d"}.filter(:a, :b) # =>  {a: "1", b: "b"}

또는

{a: "1", b: "b", c: "c", d: "d"}.filter(/^a/)  # =>  {a: "1"}

5
params.select{ |k,v| k =~ /choice\d/ }.map{ |k,v| v}.join("\t")

4

보너스 질문에 관해서는 :

  1. 다음 #select과 같은 방법으로 출력 한 경우 (2 요소 배열 목록) :

    [[:choice1, "Oh look, another one"], [:choice2, "Even more strings"], [:choice3, "But wait"]]

    그런 다음이 결과를 가져 와서 다음을 실행하십시오.

    filtered_params.join("\t")
    # or if you want only values instead of pairs key-value
    filtered_params.map(&:last).join("\t")
  2. 다음 #delete_if과 같은 방법으로 출력 한 경우 (해시) :

    {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}

    그때:

    filtered_params.to_a.join("\t")
    # or
    filtered_params.values.join("\t")

훌륭한 솔루션. 한 가지 질문 : filters_params.map (& : last) .join ( "\ t")이 filtered_params.map (| i | i.last) .join ( "\ t")의 약자입니까? 그렇다면 '마지막'이란 무엇입니까? & : value를 사용하여 해시 값을 얻을 수 있습니까?
데릭

map인수로서 블록을 취합니다. 당신과 함께 즉석에서 블록을 만들 수 있습니다 &:method블록을 만들 것입니다 건설, {|v| v.method }. 필자의 경우 &:last배열 (2 요소 배열) 인수에서 호출되었습니다. 당신이 그것으로 재생하려면, 무엇을 인수 당신이 블록으로받는 첫 번째 검사가 (당신이 체인 방법이 없습니다 (1 개 인수를 얻을! 여러 인수로를 해체하지 않음)과 1 개 방법을 찾으려고 &:method)을 반환하는 무엇을 필요. 가능하다면 바로 가기를 사용할 수 있습니다. 그렇지 않은 경우 전체 블록이 필요합니다
MBO

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

choices = params.select { |key, value| key.to_s[/^choice\d+/] }
#=> {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}

0

비슷한 문제가 있습니다. 제 경우에는 솔루션이 키가 기호가 아니더라도 작동하는 하나의 라이너 였지만 배열에 기준 키가 있어야합니다.

criteria_array = [:choice1, :choice2]

params.select { |k,v| criteria_array.include?(k) } #=> { :choice1 => "Oh look another one",
                                                         :choice2 => "Even more strings" }

다른 예시

criteria_array = [1, 2, 3]

params = { 1 => "A String",
           17 => "Oh look, another one",
           25 => "Even more strings",
           49 => "But wait",
           105 => "The last string" }

params.select { |k,v| criteria_array.include?(k) } #=> { 1 => "A String"}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.