Ruby: Easiest Way to Filter Hash Keys?

前端 未结 13 1189

I have a hash that looks something like this:

params = { :irrelevant => \"A String\",
           :choice1 => \"Oh look, another one\",
           :choi         


        
相关标签:
13条回答
  • 2020-11-30 18:28

    Put this in an initializer

    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
    

    Then you can do

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

    or

    {a: "1", b: "b", c: "c", d: "d"}.filter(/^a/)  # =>  {a: "1"}
    
    0 讨论(0)
  • 2020-11-30 18:29

    If you work with rails and you have the keys in a separate list, you can use the * notation:

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

    As other answers stated, you can also use slice! to modify the hash in place (and return the erased key/values).

    0 讨论(0)
  • 2020-11-30 18:30

    This is a one line to solve the complete original question:

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

    But most the solutions above are solving a case where you need to know the keys ahead of time, using slice or simple regexp.

    Here is another approach that works for simple and more complex use cases, that is swappable at runtime

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

    Now not only this allows for more complex logic on matching the keys or the values, but it is also easier to test, and you can swap the matching logic at runtime.

    Ex to solve the original issue:

    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"
    
    0 讨论(0)
  • 2020-11-30 18:33
    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 讨论(0)
  • 2020-11-30 18:35

    The easiest way is to include the gem 'activesupport' (or gem 'active_support').

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

    0 讨论(0)
  • 2020-11-30 18:38

    In Ruby, the Hash#select is a right option. If you work with Rails, you can use Hash#slice and Hash#slice!. e.g. (rails 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}
    
    0 讨论(0)
提交回复
热议问题