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

A

Aryk Grosz

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.
 
C

Chris Shea

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.

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
 
C

Chris Shea

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.

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)}
 
S

Siep Korteling

Chris said:

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
 
E

Einar Magnús Boson

Whenever Im coding I usually come across having to create a new hash
from the key/value pairs of another hash.

For example,

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

I want to do something like this

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

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

If anybody thinks that I shouldnt even be getting into this =20
situation in
the first place, please let me know that as well.

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

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

HTH,
Chris


That is ridiculously inefficient and weird :)

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

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

p selected

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




Einar Magn=FAs Boson
+354-661 1649
(e-mail address removed)
(e-mail address removed)
 
E

Einar Magnús Boson

Whenever Im coding I usually come across having to create a new hash
from the key/value pairs of another hash.

For example,

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

I want to do something like this

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

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

If anybody thinks that I shouldnt even be getting into this =20
situation in
the first place, please let me know that as well.

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

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

HTH,
Chris


That is ridiculously inefficient and weird :)

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

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

p selected

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




Einar Magn=FAs Boson
+354-661 1649
(e-mail address removed)
(e-mail address removed)

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

Einar Magn=FAs Boson
+354-661 1649
(e-mail address removed)
(e-mail address removed)
 
E

Einar Magnús Boson

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}]

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}
 
T

Todd Benson

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 =3D {:a =3D> 1,
:b =3D> 2,
:c =3D> 3,
:d =3D> 4,
:str =3D> "test"}

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


p selected

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

You will get nils for none existing keys that way.

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

If that's what you want, great.

Todd
 
E

Einar Magnús Boson

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

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

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


p selected

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

You will get nils for none existing keys that way.

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

If that's what you want, great.

Todd

if that is a problem it's easy to fix


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

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

selected =3D find.inject({}) {
|result, key|
val=3Dhash[key]
result[key]=3Dval if val
result }

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

given: f, h =3D 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
 
T

Todd Benson

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 =3D {:a =3D> 1,
:b =3D> 2,
:c =3D> 3,
:d =3D> 4,
:str =3D> "test"}

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


p selected

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

You will get nils for none existing keys that way.

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

If that's what you want, great.

Todd

if that is a problem it's easy to fix


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

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

selected =3D find.inject({}) {
|result, key|
val=3Dhash[key]
result[key]=3Dval if val
result }

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

given: f, h =3D find.size, hash.size
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 analysi=
s.
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
 
L

Luc Heinrich

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}
 
B

Brian Candler

Aryk said:
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 :-(
 
T

Todd Benson

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 =3D {:a =3D> 1,
:b =3D> 2,
:c =3D> 3,
:d =3D> 4,
:str =3D> "test"}

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


p selected

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

You will get nils for none existing keys that way.

selected =3D [:whatever].inject({}){|result, key| =3D hash[key]; result}
#=3D> {:whatever =3D> 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 =3D Hash[:a, 1, :b, 2]
p [:missing_key].inject({}) {|result, key| result[key] =3D h[key]; result}
=3D> {:missing_key =3D> 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
 
T

Trans

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

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

class Hash
=A0 =A0 =A0def from_keys(*keys)
=A0 =A0 =A0 =A0 =A0selected_values =3D self.values_at(*keys)
=A0 =A0 =A0 =A0 =A0selected_key_values =3D keys.zip(selected_values)
=A0 =A0 =A0 =A0 =A0Hash[*selected_key_values.flatten]
=A0 =A0 =A0end
end

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

=3D> {:a=3D>1, :b=3D>2}

require 'facets/hash/slice'

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top