stevec
stevec

Reputation: 52258

Filter an array of hashes to keep only those elements whose hashes contain a key contained in an array?

Here we have an array of hashes. How can we keep only the hashes for which at least one key is contained in an array?

# array of hashes
@arr_of_h = [{first: 10},
{second: 10, secondB: 10},
{third: 10, thirdB: 10}]

# array
arr = ["first", "third"]

Desired output

@output = [{:first=>10}, {:third=>10, :thirdB=>10}]

Upvotes: 4

Views: 2839

Answers (3)

user11350468
user11350468

Reputation: 1407

Try the below with Array select:

select will return an array containing elements which match the condition provided on the block.

Hope the hash on every index of the array will include only one key

 @arr_of_h.select{ |element| arr.include?(element.keys.first.to_s) }

EDIT #1

To find the record which at least has one key contained in an array

We can achieve it through Array select and Array any?

 @arr_of_h.select{ |element| element.keys.any? {|element| arr.include?(element.to_s) }}

Upvotes: 4

benjessop
benjessop

Reputation: 1959

This would work if you have hashes in your array with more than one key and you wanted to keep hashes if one of the hash keys was present the 'keep' array.

arr_of_h.delete_if { |hash| (arr & (hash.keys.map(&:to_s))).empty? }

As Cary rightfully pointed out, this does mutate the array. If you do not wish to mutate the array:

arr_of_h.select { |hash| (arr & (hash.keys.map(&:to_s))).any? }

Upvotes: 2

Timur Shtatland
Timur Shtatland

Reputation: 12347

If speed is important, and if the array has many elements, which translates to many lookups, use a set. Also, for speed, convert the array to symbols at the beginning, rather than converting it in the select loop. Note that for the particular case described by the OP, the speed improvements would be tiny if any, since all inputs are small.

require "Set"
@arr_of_h = [{first: 10}, {second: 10}, {third: 10}]
arr = ["first", "third"]
arr_as_set = arr.map(&:to_sym).to_set
@output = @arr_of_h.select { |h| arr_as_set.include?(h.keys.first) }
puts @output
# Prints: 
# {:first=>10}
# {:third=>10}

SEE ALSO:

Set#include? docs
Set#include? vs. Array#include?
More on Set#include?
Speed comparisons for arrays, sets, and hashes, with benchmarks

Upvotes: 2

Related Questions