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
Heroku Not Sending Email With Gmail Smtp
How to Use Define_Method to Create Class Methods
Find Number of Months Between Two Dates in Ruby on Rails
How to Redirect Stderr and Stdout to File For a Ruby Script
Flutter Pod Install Problem - Undefined Method 'Each_Child' For #≪Dir:0X00007Fa6F7E2Ec80≫
Rails Assets:Precompile Yarn Executable Was Not Detected in the System
A Method with an Optional Parameter
Turning Long Fixed Number to Array Ruby
Rails: Is There a Rails Trick to Adding Commas to Large Numbers
How to Implement a Friendship Model in Rails 3 for a Social Networking Application
Nomethoderror When Trying to Invoke Helper Method from Rails Controller
How to Calculate the Day of the Week of a Date in Ruby
How to Edit or Write on Existing Pdf With Ruby
How to Avoid Trailing Empty Items Being Removed When Splitting Strings
Error Sudo: Gem: Command Not Found