programing

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

muds 2023. 5. 31. 18:34
반응형

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

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

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

그리고 선택+int가 아닌 모든 키를 거부할 수 있는 간단한 방법을 원합니다.선택1 또는 선택1 ~ 선택10일 수 있습니다.다양합니다.

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

보너스:

해시를 탭(\t)을 구분 기호로 사용하는 문자열로 변환합니다.저는 이것을 했지만, 그것은 몇 줄의 코드를 필요로 했습니다.보통 루비치안의 달인들은 한 줄 정도로 그것을 할 수 있습니다.

원본 답변으로 편집: 이 답변이 선택된 답변임에도 불구하고(이 의견을 제시한 시점 기준) 이 답변의 원본 버전은 구식입니다.

저는 제가 했던 것처럼 다른 사람들이 이 답변에 의해 소외되는 것을 방지하기 위해 여기에 업데이트를 추가합니다.

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

예:

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

편집 종료.다음은 Ruby < 2.5에 Rails가 없는 경우 유용할 것으로 생각되는 원래 답변입니다. 이 시점에서 이 경우는 매우 드물 것으로 생각됩니다.


Ruby를 사용하는 경우 다음을 사용할 수 있습니다.select방법.정규식 일치를 수행하려면 키를 기호에서 문자열로 변환해야 합니다.이렇게 하면 선택 항목만 포함된 새 해시가 제공됩니다.

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"}

버그가 있을 수 있는 다른 기능을 선언하는 것은 가치가 없다고 생각하며, 지난 몇 년 동안 수정된 방법을 사용하는 것이 좋습니다.

레일 관련 작업을 수행하고 별도의 목록에 키가 있는 경우*표기법:

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

다른 답변에서 언급한 것처럼 다음을 사용할 수도 있습니다.slice!해시를 수정하고 지워진 키/값을 반환합니다.

해시 슬라이스 포함

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

# If you have an array of keys you want to limit to, you should splat them:
valid_keys = [:mass, :velocity, :time]
search(options.slice(*valid_keys))

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

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

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

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

해결하는 것입니다.slice또단 순는 regexp.

런타임에 교환 가능한 간단하고 복잡한 사용 사례에 적합한 또 다른 접근 방식이 있습니다.

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"

포함:

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

나머지 해시를 원하는 경우:

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

아니면 그냥 열쇠를 원한다면,

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

이것을 이니셜라이저에 넣습니다.

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"}
params.select{ |k,v| k =~ /choice\d/ }.map{ |k,v| v}.join("\t")

보너스 질문의 경우:

  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")
    
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"}

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

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"}

언급URL : https://stackoverflow.com/questions/7430343/ruby-easiest-way-to-filter-hash-keys

반응형