Forum: Ruby function to select only certain key/value pairs from hash?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
525128e48ca2b4c7fb6176ea166fccfd?d=identicon&s=25 Eric G. (gotskill10)
on 2008-11-17 23:54
Whenever Im coding I usually come across having to create a new hash
from the key/value pairs of another hash.

For example,

hash = {:a => 1, :b => 2, :c => 3}

I want to do something like this

hash.from_keys(:a,:b) => {:a=> 1, :b=> 2}

Is there something like this already? Perhaps in the facets library? I
looked and couldn't find anything.

If anybody thinks that I shouldnt even be getting into this situation in
the first place, please let me know that as well.
017e05d1a49ffa59ea03e149e7af720b?d=identicon&s=25 Chris Shea (chrisshea)
on 2008-11-18 00:50
(Received via mailing list)
On Nov 17, 3:50 pm, Aryk Grosz <tennisbum2...@hotmail.com> wrote:
>
> Is there something like this already? Perhaps in the facets library? I
> looked and couldn't find anything.
>
> If anybody thinks that I shouldnt even be getting into this situation in
> the first place, please let me know that as well.
> --
> Posted viahttp://www.ruby-forum.com/.

Hmm... I thought there was a built in method for that.  Here's one
way:

hash = {:a => 1, :b => 2, :c => 3}
Hash[*hash.select {|k,v| [:a,:b].include?(k)}.flatten]  #=>
{:b=>2, :a=>1}

HTH,
Chris
017e05d1a49ffa59ea03e149e7af720b?d=identicon&s=25 Chris Shea (chrisshea)
on 2008-11-18 00:55
(Received via mailing list)
On Nov 17, 4:46 pm, Chris Shea <cms...@gmail.com> wrote:
>
> > Posted viahttp://www.ruby-forum.com/.
>
> Hmm... I thought there was a built in method for that.  Here's one
> way:
>
> hash = {:a => 1, :b => 2, :c => 3}
> Hash[*hash.select {|k,v| [:a,:b].include?(k)}.flatten]  #=>
> {:b=>2, :a=>1}
>
> HTH,
> Chris

Yeah, or the simpler: hash.reject {|k,v| ![:a,:b].include?(k)}
D7908f05c89e965f6bc5308ad6f41256?d=identicon&s=25 Siep Korteling (steenslag)
on 2008-11-18 00:59
Chris Shea wrote:
> On Nov 17, 4:46�pm, Chris Shea <cms...@gmail.com> wrote:
>>
>> > Posted viahttp://www.ruby-forum.com/.
>>
>> Hmm... I thought there was a built in method for that. �Here's one
>> way:
>>
>> hash = {:a => 1, :b => 2, :c => 3}
>> Hash[*hash.select {|k,v| [:a,:b].include?(k)}.flatten] �#=>
>> {:b=>2, :a=>1}
>>
>> HTH,
>> Chris
>
> Yeah, or the simpler: hash.reject {|k,v| ![:a,:b].include?(k)}

 And indeed, it's in facets:

require 'facets'
p hash.slice(:a,:b)

# However, this breaks on

hash.slice(:a,:d)

# and the hash.reject does not

hth,

Siep

Siep
87349a7a95b3f2e83c20194ef122885c?d=identicon&s=25 Einar Magnús Boson (Guest)
on 2008-11-18 02:38
(Received via mailing list)
On 17.11.2008, at 23:46 , Chris Shea wrote:

>> hash.from_keys(:a,:b) => {:a=> 1, :b=> 2}
>
> Hmm... I thought there was a built in method for that.  Here's one
> way:
>
> hash = {:a => 1, :b => 2, :c => 3}
> Hash[*hash.select {|k,v| [:a,:b].include?(k)}.flatten]  #=>
> {:b=>2, :a=>1}
>
> HTH,
> Chris
>


That is ridiculously inefficient and weird :)

hash = {:a => 1, :b => 2, :c => 3}

selected = [:a, :b].map{|key|{key, hash[key]}}

p selected

# >> [{:a=>1}, {:b=>2}]




Einar Magnús Boson
+354-661 1649
einarmagnus@tistron.se
einar.boson@gmail.com
87349a7a95b3f2e83c20194ef122885c?d=identicon&s=25 Einar Magnús Boson (Guest)
on 2008-11-18 02:41
(Received via mailing list)
On 18.11.2008, at 01:34 , Einar Magnús Boson wrote:

>>>
>>> the first place, please let me know that as well.
>> HTH,
> p selected
>
>

and now I see that I was stupd.
heh. nevermind <.<

Einar Magnús Boson
+354-661 1649
einarmagnus@tistron.se
einar.boson@gmail.com
87349a7a95b3f2e83c20194ef122885c?d=identicon&s=25 Einar Magnús Boson (Guest)
on 2008-11-18 02:48
(Received via mailing list)
>>
>> # >> [{:a=>1}, {:b=>2}]
>>
>>
>
> and now I see that I was stupd.
> heh. nevermind <.<
>

This is what I meant to do, a lot more efficient to look up the values
you're lookin for than looping through all keys for every element.

hash = {:a => 1,
      :b => 2,
      :c => 3,
      :d => 4,
      :str => "test"}

selected = [:a, :d, :str].inject({}){|result, key| result[key]=
hash[key];result}


p selected

# >> {:str=>"test", :a=>1, :d=>4}

.
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2008-11-18 06:04
(Received via mailing list)
On Mon, Nov 17, 2008 at 7:44 PM, Einar Magnús Boson
<einarmagnus@tistron.se> wrote:
> hash[key];result}
>
>
> p selected
>
> # >> {:str=>"test", :a=>1, :d=>4}

You will get nils for none existing keys that way.

selected = [:whatever].inject({}){|result, key| = hash[key]; result}
#=> {:whatever => nil}

If that's what you want, great.

Todd
87349a7a95b3f2e83c20194ef122885c?d=identicon&s=25 Einar Magnús Boson (Guest)
on 2008-11-18 06:28
(Received via mailing list)
On 18.11.2008, at 05:00 , Todd Benson wrote:

>>                       :str => "test"}
>
> selected = [:whatever].inject({}){|result, key| = hash[key]; result}
> #=> {:whatever => nil}
>
> If that's what you want, great.
>
> Todd
>

if that is a problem it's easy to fix


hash = {:a => 1,
         :b => 2,
         :c => 3,
         :d => 4,
         :str => "test"}

find = [:a, :d, :str, :extra]

selected = find.inject({}) {
      |result, key|
      val=hash[key]
      result[key]=val if val
      result }

p selected
# >> {:str=>"test", :a=>1, :d=>4}

given: f, h = find.size, hash.size
This method is O(f) because hash lookup is O(1)
The other way it's O(f*h).
so if the hash is big it should make a difference.

If the elements always are just a few it doesn't really matter.

einarmagnus
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2008-11-18 06:48
(Received via mailing list)
On Mon, Nov 17, 2008 at 11:23 PM, Einar Magnús Boson
<einarmagnus@tistron.se> wrote:
>>>                      :b => 2,
>>> # >> {:str=>"test", :a=>1, :d=>4}
>
>
> This method is O(f) because hash lookup is O(1)
> The other way it's O(f*h).

Good point.  The use of #inject, though, may cloud that performance
analysis.

> so if the hash is big it should make a difference.
>
> If the elements always are just a few it doesn't really matter.

I'm an #inject sort of guy so I like the way you approach this.  But,
there must be a reason why you don't prefer the negative #reject that
seems to work for most people.

I might benchmark this, but I think object creation and destruction
might outweigh the O(f*h).

Todd
8a85c693f13ef7cb542ef94d2a403d4d?d=identicon&s=25 Luc Heinrich (Guest)
on 2008-11-18 09:35
(Received via mailing list)
On 17 nov. 08, at 23:50, Aryk Grosz wrote:

> hash.from_keys(:a,:b) => {:a=> 1, :b=> 2}

I tried to resist making this a one-liner and went for the clean and
explicit way :)

class Hash
     def from_keys(*keys)
         selected_values = self.values_at(*keys)
         selected_key_values = keys.zip(selected_values)
         Hash[*selected_key_values.flatten]
     end
end

hash = {:a => 1, :b => 2, :c => 3}
p hash.from_keys(:a, :b)

=> {:a=>1, :b=>2}
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2008-11-18 11:06
Aryk Grosz wrote:
> I want to do something like this
>
> hash.from_keys(:a,:b) => {:a=> 1, :b=> 2}

In ruby1.9, Hash#select returns another Hash. But you'd still be
iterating the 'wrong way' (that is, iterating through the hash and doing
a linear search through the keys)

Personally I'd go with:

class Hash
  def from_keys(*keys)
    keys.inject({}) { |h,k| h[k] = self[k] if has_key?(k); h }
  end
end

hash = {:a => 1, :b => 2, :c => 3}
p hash.from_keys(:a, :b)

With ruby19 you can do:

    keys.each_with_object({}) { |k,h| h[k] = self[k] if has_key?(k) }

which is more keystrokes but maybe the teeniest bit more efficient. But
I hate each_with_object on the principle that its arguments are the
opposite way round to inject :-(
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2008-11-18 11:44
(Received via mailing list)
On Mon, Nov 17, 2008 at 11:04 PM, Todd Benson <caduceass@gmail.com>
wrote:
>>
> selected = [:whatever].inject({}){|result, key| = hash[key]; result}
> #=> {:whatever => nil}

I'm not apologizing for the missing result[key] on the lhs, though I
know it's pretty important to many to have working code in a message.
Anyone with a half a brain knows what I meant, but just to be clean...

h = Hash[:a, 1, :b, 2]
p [:missing_key].inject({}) {|result, key| result[key] = h[key]; result}
=> {:missing_key => nil}

In any case, reducing a hash (being selective) I think is best done
using a reject with a negative in the block (as per Chris Shea's
second example).

Todd
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Thomas Sawyer (7rans)
on 2008-11-18 13:44
(Received via mailing list)
On Nov 18, 3:31 am, Luc Heinrich <l...@honk-honk.com> wrote:
>          selected_key_values = keys.zip(selected_values)
>          Hash[*selected_key_values.flatten]
>      end
> end
>
> hash = {:a => 1, :b => 2, :c => 3}
> p hash.from_keys(:a, :b)
>
> => {:a=>1, :b=>2}

require 'facets/hash/slice'

T.
This topic is locked and can not be replied to.