Ruby: Easiest Way to Filter Hash Keys

Ruby: Easiest Way to Filter Hash Keys?

Edit to original answer: Even though this is answer (as of the time of this comment) is the selected answer, the original version of this answer is outdated.

I'm adding an update here to help others avoid getting sidetracked by this answer like I did.

As the other answer mentions, Ruby >= 2.5 added the Hash#slice method which was previously only available in Rails.

Example:

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

End of edit. What follows is the original answer which I guess will be useful if you're on Ruby < 2.5 without Rails, although I imagine that case is pretty uncommon at this point.


If you're using Ruby, you can use the select method. You'll need to convert the key from a Symbol to a String to do the regexp match. This will give you a new Hash with just the choices in it.

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

or you can use delete_if and modify the existing Hash e.g.

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

or if it is just the keys and not the values you want then you can do:

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

and this will give the just an Array of the keys e.g. [:choice1, :choice2, :choice3]

Is there an elegant way in Ruby to filter a hash of arrays of hashses?

a={
"foo": [{state: "on"}, {state: "off"}, {state: "on"}],
"bar": [{state: "off"}, {state: "off"}, {state: "on"}],
"baz": [{state: "on"}, {state: "on"}, {state: "on"}]
}

Code

p a.transform_values{|arr| arr.select{|h|h[:state].eql?'on'}}

Result

{:foo=>[{:state=>"on"}, {:state=>"on"}], :bar=>[{:state=>"on"}], :baz=>[{:state=>"on"}, {:state=>"on"}, {:state=>"on"}]}

How to filter specific key-value pair in hash that matches an array

Rails provides a Hash method that makes filtering quite simple - slice(). In combination with the splat operator * you could write the following code.

fields = schema.map { |e| e[:name] }

input.slice(*fields)
#=> {"fname"=>"XXX", "email"=>"xxx@example.com"}

Select key value pairs from a hash whose keys belong to an array

Try this:

hsh.select{ |k, v| keys.include?(k) } 

Selecting certain keys in a Hash

Given:

h = {}; (1..100).each {|v| h[v.to_s] = v }

You can use the memory_profiler gem to measure allocations. Use something like MemoryProfiler.report { code }.total_allocated.

If memory allocations are really at a premium here, your approach of preallocating the result and then enumerating with #each is what you want. The reason for this is that Ruby optimizes Hash#each for blocks with an arity of 2, so that a new array isn't constructed per loop. The only allocation in this approach is the results array.

MemoryProfiler.report { r = [] ; h.each {|k, v| r << k if v > 1 } }.total_allocated
# => 1

Using #reduce, OTOH, results in an allocation per loop because you break the arity rule:

MemoryProfiler.report { h.reduce([]) {|agg, (k, v)| agg << k if v > 1 ; agg } }.total_allocated
# => 101

If you want something more "self-contained" and are willing to sacrifice an extra allocation, you'll want to use #each_key (which does create an intermediate array of keys) and then index into the hash to test each value.

h.each_key.select {|k| h[k] > 1 }

Ruby Hash Whitelist Filter

Maybe this it what you want.

wanted_keys = %w[one two]
x = { "one" => "one", "two" => "two", "three" => "three"}
x.select { |key,_| wanted_keys.include? key }

The Enumerable mixin which is included in e.g. Array and Hash provides a lot of useful methods like select/reject/each/etc.. I suggest that you take a look at the documentation for it with ri Enumerable.

i want to make a group Hash by keys and add values

I'd use the each_with_object method.
(key, value) here is a deconstruction of each key/value pair like 93=>1, hash is an intermediate object to store the result.

data.each_with_object({}) do |(key, value), hash|
result_key =
case key
when 10..19 then 1
when 20..29 then 2
when 90..99 then 9
end
next if result_key.nil?
hash[result_key] ||= 0
hash[result_key] += value
end

For the provided input I got {9=>2, 2=>7856, 1=>267}

UPD

A shorter solution suggested by Holger Just and Stefan in the comments below.

data.each_with_object(Hash.new(0)) do |(key, value), hash|
hash[key / 10] += value
end

With Hash.new(0) the initial object will be a hash with the default value 0

> hash = Hash.new(0)
=> {}
> hash[1]
=> 0

Replace all HASH keys with another key

If the variable hash holds the hash given in the example, and that object is to be mutated, one can write the following.

hash[:cars].each { |h| h.transform_keys! { |k| k == :seats ? :people : k } }
#=> [
# {
# :name=>"Diablo",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Raptor",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Testarossa",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# }
# ]

​so now

hash
#=> {
# :name=>"Josh",
# :surname=>"Simon",
# :cars=>[
# {
# :name=>"Diablo",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Raptor",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Testarossa",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# }
# ]
# }

See Hash#transform_keys!.


If the hash is not to be mutated you could operate on a deep copy. One of doing that is to use the methods Marshal::load and Marshal::dump:

h = Marshal.load(Marshal.dump(hash))
#=> {
# :name=>"Josh",
# :surname=>"Simon",
# :cars=>[
# {
# :name=>"Diablo",
# :seats=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Raptor",
# :seats=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Testarossa",
# :seats=>[{:name=>"Josh", :surname=>"Simon"}]
# }
# ]
# }

Note that if we alter h by writing, for example,

h[:name] = "Lola"
h[:cars][1][:seats][0] = "cats"

we may verify that hash is unchanged.



Related Topics



Leave a reply



Submit