Why can't I make a new hash from selected keys

2019-06-23 15:05发布

This has been bugging me for a while. It's not a difficult thing, but I don't know why there's no easy way to do it already, and I bet there is and I don't see it.

I just want to take a hash, like this:

cars = {:bob => 'Pontiac', :fred => 'Chrysler', 
        :lisa => 'Cadillac', :mary => 'Jaguar'}

and do something like

cars[:bob, :lisa]

and get

{:bob => 'Pontiac', :lisa => 'Cadillac'}

I did this, which works fine:

class Hash
  def pick(*keys)
    Hash[select { |k, v| keys.include?(k) }]
  end
end

ruby-1.8.7-p249 :008 > cars.pick(:bob, :lisa)
=> {:bob=>"Pontiac", :lisa=>"Cadillac"} 

There's obviously a zillion easy ways to do this, but I'm wondering if there's something built in I've missed, or a good an un-obvious reason it's not a standard and normal thing? Without it, I wind up using something like:

chosen_cars = {:bob => cars[:bob], :lisa => cars[:lisa]}

which isn't the end of the world, but it's not very pretty. It seems like this should be part of the regular vocabulary. What am I missing here?

(related questions, include this: Ruby Hash Whitelist Filter) (this blog post has precisely the same result as me, but again, why isn't this built in? http://matthewbass.com/2008/06/26/picking-values-from-ruby-hashes/ )

update:

I'm using Rails, which has ActiveSupport::CoreExtensions::Hash::Slice, which works exactly as I want it to, so problem solved, but still... maybe someone else will find their answer here :)

标签: ruby hash
5条回答
看我几分像从前
2楼-- · 2019-06-23 15:17
{ height: '178cm', weight: '181lbs', salary: '$2/hour' }.select { |k,v| [:height, :weight].include?(k)  }
查看更多
Viruses.
3楼-- · 2019-06-23 15:23

I've always thought that was a weird omission as well, but there really is no simple, standard method for that.

Your example above might be unnecessarily slow because it iterates over all the hash entries whether we need them or not, and then repeatedly searches through the keys parameter array. This code should be a bit faster (assuming that would ever matter -- and I haven't tried to benchmark it).

class Hash
  def pick(*keys)
    values = values_at(*keys)
    Hash[keys.zip(values)]
  end
end
查看更多
我命由我不由天
4楼-- · 2019-06-23 15:30

select deserves at least to be mentioned:

cars = {:bob => 'Pontiac', :fred => 'Chrysler', 
        :lisa => 'Cadillac', :mary => 'Jaguar'}
people = [:bob, :lisa]

p cars.select{|k, _| people.include?(k)}
#=> {:bob=>"Pontiac", :lisa=>"Cadillac"}
查看更多
该账号已被封号
5楼-- · 2019-06-23 15:31

Just to help others, some years after the fact:

Slice works nicely to do what I wanted.

> cars.slice(:bob, :lisa)
=> {:bob=>"Pontiac", :lisa=>"Cadillac"} 
查看更多
爷的心禁止访问
6楼-- · 2019-06-23 15:33

Ruby makes it possible to add that feature without much pain:

class Hash
  alias old_accessor :[]

  def [](*key)
    key.is_a?(Array) ? self.dup.delete_if{|k, v| !key.include? k} : old_accessor(key)
  end
end

I hope this helps. I know it's not a built-in feature.

查看更多
登录 后发表回答