Home > database >  Fetch hash keys that are present in another array
Fetch hash keys that are present in another array

Time:08-01

I have a use case where I want to fetch keys from a hash which are present in a separate array.

Example:

h = {"video"=>"MP4", "audio"=>"MP3", "sharing"=>"NONE", "mix"=>"NONE"}
a = ["video", "audio", "txt"]

Expected result:

["video", "audio"]

I am trying to use slice method on hash but it is not working for me:

h.slice(a)

Any help would be appreciated.

CodePudding user response:

The reason you attempt doesn't work is that hash keys can actually be any kind of object and Hash#slice will compare them by identity:

# its a hash with array keys - crazy stuff.
=> {[1, 2, 3]=>"a", [3, 4, 5]=>"b"}
irb(main):012:0> hash.slice([1,2,3])
=> {[1, 2, 3]=>"a"}
# [1,2,3] and [1,2] are not equal
irb(main):013:0> hash.slice([1,2])
=> {}
irb(main):014:0> hash.slice([3,4,5])
=> {[3, 4, 5]=>"b"}
irb(main):015:0> 

An array will never be equal to a string in Ruby.

If you want to convert an array into a list of arguments to pass to a method use the splat operator (*):

irb(main):022:0> h = {"video"=>"MP4","audio"=>"MP3", "sharing"=>"NONE", "mix"=>"NONE"}
=> {"video"=>"MP4", "audio"=>"MP3", "sharing"=>"NONE", "mix"=>"NONE"}
irb(main):023:0> a = ["video", "audio", "txt"]
=> ["video", "audio", "txt"]
irb(main):024:0> h.slice(*a)
=> {"video"=>"MP4", "audio"=>"MP3"}

CodePudding user response:

You can do an intersection using &. For example:

2.7.2 :001 > h = {"video"=>"MP4", "audio"=>"MP3", "sharing"=>"NONE", "mix"=>"NONE"}
 => {"video"=>"MP4", "audio"=>"MP3", "sharing"=>"NONE", "mix"=>"NONE"}
2.7.2 :002 > a = ["video", "audio", "txt"]
 => ["video", "audio", "txt"]
2.7.2 :003 > h.keys & a
 => ["video", "audio"]
2.7.2 :004 >

The explanation is: you got array of keys from hash(h.keys) and them get the intersection between array of keys and the array using &

CodePudding user response:

You can try filter with a proc (which checks for h key inclusion):

a.filter(&h.method(:key?))

which is essentially the same as using a block:

a.filter { |a_i| h.key?(a_i) }

You can also use intersection with keys (this approach kind of steps on other answers with & operator here):

a.intersection(h.keys)

and you're going to get the following result with any of the solutions above:

=> ["video", "audio"]
  • Related