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

Discussion in 'Ruby' started by Aryk Grosz, Nov 17, 2008.

  1. Aryk Grosz

    Aryk Grosz Guest

    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.
    --
    Posted via http://www.ruby-forum.com/.
     
    Aryk Grosz, Nov 17, 2008
    #1
    1. Advertising

  2. Aryk Grosz

    Chris Shea Guest

    On Nov 17, 3:50 pm, Aryk Grosz <> wrote:
    > 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.
    > --
    > 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
     
    Chris Shea, Nov 17, 2008
    #2
    1. Advertising

  3. Aryk Grosz

    Chris Shea Guest

    On Nov 17, 4:46 pm, Chris Shea <> wrote:
    > On Nov 17, 3:50 pm, Aryk Grosz <> wrote:
    >
    >
    >
    > > 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.
    > > --
    > > 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)}
     
    Chris Shea, Nov 17, 2008
    #3
  4. Re: function to select only certain key/value pairs from has

    Chris Shea wrote:
    > On Nov 17, 4:46�pm, Chris Shea <> 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

    --
    Posted via http://www.ruby-forum.com/.
     
    Siep Korteling, Nov 17, 2008
    #4
  5. On 17.11.2008, at 23:46 , Chris Shea wrote:

    > On Nov 17, 3:50 pm, Aryk Grosz <> wrote:
    >> 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.
    >> --
    >> Posted viahttp://www.ruby-forum.com/.

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

     
    Einar Magnús Boson, Nov 18, 2008
    #5
  6. On 18.11.2008, at 01:34 , Einar Magn=FAs Boson wrote:

    >
    > On 17.11.2008, at 23:46 , Chris Shea wrote:
    >
    >> On Nov 17, 3:50 pm, Aryk Grosz <> wrote:
    >>> 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.
    >>> --
    >>> Posted viahttp://www.ruby-forum.com/.

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


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

    Einar Magn=FAs Boson
    +354-661 1649

     
    Einar Magnús Boson, Nov 18, 2008
    #6
  7. >>>
    >>
    >>
    >> 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}
     
    Einar Magnús Boson, Nov 18, 2008
    #7
  8. Aryk Grosz

    Todd Benson Guest

    On Mon, Nov 17, 2008 at 7:44 PM, Einar Magn=FAs Boson
    <> wrote:
    > 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
     
    Todd Benson, Nov 18, 2008
    #8
  9. On 18.11.2008, at 05:00 , Todd Benson wrote:

    > On Mon, Nov 17, 2008 at 7:44 PM, Einar Magn=FAs Boson
    > <> wrote:
    >> 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
     
    Einar Magnús Boson, Nov 18, 2008
    #9
  10. Aryk Grosz

    Todd Benson Guest

    On Mon, Nov 17, 2008 at 11:23 PM, Einar Magn=FAs Boson
    <> wrote:
    >
    > On 18.11.2008, at 05:00 , Todd Benson wrote:
    >
    >> On Mon, Nov 17, 2008 at 7:44 PM, Einar Magn=FAs Boson
    >> <> wrote:
    >>>
    >>> 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
     
    Todd Benson, Nov 18, 2008
    #10
  11. Aryk Grosz

    Luc Heinrich Guest

    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}

    --
    Luc Heinrich -
     
    Luc Heinrich, Nov 18, 2008
    #11
  12. Re: function to select only certain key/value pairs from has

    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 :-(
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Nov 18, 2008
    #12
  13. Aryk Grosz

    Todd Benson Guest

    On Mon, Nov 17, 2008 at 11:04 PM, Todd Benson <> wrote:
    > On Mon, Nov 17, 2008 at 7:44 PM, Einar Magn=FAs Boson
    > <> wrote:
    >> 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
     
    Todd Benson, Nov 18, 2008
    #13
  14. Aryk Grosz

    Trans Guest

    On Nov 18, 3:31=A0am, Luc Heinrich <> wrote:
    > On 17 nov. 08, at 23:50, Aryk Grosz wrote:
    >
    > > 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.
     
    Trans, Nov 18, 2008
    #14
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Florian Lindner

    key - key pairs

    Florian Lindner, Jun 23, 2005, in forum: Python
    Replies:
    8
    Views:
    582
    Paul McGuire
    Jun 24, 2005
  2. Markus Dehmann

    key-value pairs: key consists of 3 ints

    Markus Dehmann, Jan 15, 2006, in forum: C++
    Replies:
    13
    Views:
    650
    Richard Herring
    Jan 23, 2006
  3. Une bévue
    Replies:
    5
    Views:
    165
    Une bévue
    Aug 10, 2006
  4. Johan Martinez
    Replies:
    3
    Views:
    300
    Johan Martinez
    May 22, 2011
  5. Antonio Quinonez
    Replies:
    2
    Views:
    184
    Antonio Quinonez
    Aug 14, 2003
Loading...

Share This Page