Extracting a value from an array

Discussion in 'Ruby' started by Albert Schlef, May 2, 2008.

  1. I have the following array:

    headers = [
    { :name => 'user-agent', :value => 'blah blah' },
    { :name =>'content-type', :value => 'text/html' },
    { :name => 'pragma', :value => 'no-cache' },
    { :name =>'content-length', :value => '30' },
    { :name =>'content-type', :value => 'text/html' },
    ]

    Now, I want to extract the :value of the first header of a specific
    :name. For example, I want to extract the :value of the 'content-type'
    header.

    So I do:

    content_length = nil
    headers.each { |header|
    content_length = header[:value] if header[:name] == 'content-length'
    }

    However, this code is not very "beautiful", and I was wondering if
    there's some other, more clearer way to do this.

    (And it's fine with me if we take the :value of a 'content-type' header
    which is not necessarily the first in the array (in the code above I
    actually pick the last header). Also, I don't very much mind about
    performance because there are a few headers. I'm simply looking for a
    clear, straightforward code for this simple task.)
    --
    Posted via http://www.ruby-forum.com/.
    Albert Schlef, May 2, 2008
    #1
    1. Advertising

  2. Albert Schlef

    Jano Svitok Guest

    On Fri, May 2, 2008 at 11:02 AM, Albert Schlef <> wrote:
    > I have the following array:
    >
    > headers = [
    > { :name => 'user-agent', :value => 'blah blah' },
    > { :name =>'content-type', :value => 'text/html' },
    > { :name => 'pragma', :value => 'no-cache' },
    > { :name =>'content-length', :value => '30' },
    > { :name =>'content-type', :value => 'text/html' },
    > ]
    >
    > Now, I want to extract the :value of the first header of a specific
    > :name. For example, I want to extract the :value of the 'content-type'
    > header.
    >
    > So I do:
    >
    > content_length = nil
    > headers.each { |header|
    > content_length = header[:value] if header[:name] == 'content-length'
    > }
    >
    > However, this code is not very "beautiful", and I was wondering if
    > there's some other, more clearer way to do this.
    >
    > (And it's fine with me if we take the :value of a 'content-type' header
    > which is not necessarily the first in the array (in the code above I
    > actually pick the last header). Also, I don't very much mind about
    > performance because there are a few headers. I'm simply looking for a
    > clear, straightforward code for this simple task.)


    headers.select {|header| header[:name] == 'content-length'}.first[:value]

    or even better:

    headers.find {|header| header[:name] == 'content-length'}[:value]
    Jano Svitok, May 2, 2008
    #2
    1. Advertising

  3. I've compressed your loop into this:

    (headers.select{ |h| h[:name] == 'content-length' }.first)[:value]

    On Fri, May 2, 2008 at 9:02 AM, Albert Schlef <> wrote:
    > I have the following array:
    >
    > headers = [
    > { :name => 'user-agent', :value => 'blah blah' },
    > { :name =>'content-type', :value => 'text/html' },
    > { :name => 'pragma', :value => 'no-cache' },
    > { :name =>'content-length', :value => '30' },
    > { :name =>'content-type', :value => 'text/html' },
    > ]
    >
    > Now, I want to extract the :value of the first header of a specific
    > :name. For example, I want to extract the :value of the 'content-type'
    > header.
    >
    > So I do:
    >
    > content_length = nil
    > headers.each { |header|
    > content_length = header[:value] if header[:name] == 'content-length'
    > }
    >
    > However, this code is not very "beautiful", and I was wondering if
    > there's some other, more clearer way to do this.
    >
    > (And it's fine with me if we take the :value of a 'content-type' header
    > which is not necessarily the first in the array (in the code above I
    > actually pick the last header). Also, I don't very much mind about
    > performance because there are a few headers. I'm simply looking for a
    > clear, straightforward code for this simple task.)
    > --
    > Posted via http://www.ruby-forum.com/.
    >
    >




    --
    Go outside! The graphics are amazing!
    Sandro Paganotti, May 2, 2008
    #3
  4. Albert Schlef

    7stud -- Guest

    Albert Schlef wrote:
    > I have the following array:
    >
    > headers = [
    > { :name => 'user-agent', :value => 'blah blah' },
    > { :name =>'content-type', :value => 'text/html' },
    > { :name => 'pragma', :value => 'no-cache' },
    > { :name =>'content-length', :value => '30' },
    > { :name =>'content-type', :value => 'text/html' },
    > ]
    >
    > Now, I want to extract the :value of the first header of a specific
    > :name. For example, I want to extract the :value of the 'content-type'
    > header.
    >
    > So I do:
    >
    > content_length = nil
    > headers.each { |header|
    > content_length = header[:value] if header[:name] == 'content-length'
    > }
    >
    > However, this code is not very "beautiful", and I was wondering if
    > there's some other, more clearer way to do this.
    >
    > (And it's fine with me if we take the :value of a 'content-type' header
    > which is not necessarily the first in the array (in the code above I
    > actually pick the last header). Also, I don't very much mind about
    > performance because there are a few headers. I'm simply looking for a
    > clear, straightforward code for this simple task.)


    headers = [
    { :name => 'user-agent', :value => 'blah blah' },
    { :name =>'content-type', :value => 'text/html' },
    { :name => 'pragma', :value => 'no-cache' },
    { :name =>'content-length', :value => '30' },
    { :name =>'content-type', :value => 'text/html' },
    ]

    content_type = ""

    headers.each do |hash|
    if hash[:name] == "content-type"
    content_type = hash[:value]
    break
    end
    end

    puts content_type
    --
    Posted via http://www.ruby-forum.com/.
    7stud --, May 2, 2008
    #4
  5. Albert Schlef

    7stud -- Guest

    Sandro Paganotti wrote:
    > I've compressed your loop into this:
    >
    > (headers.select{ |h| h[:name] == 'content-length' }.first)[:value]


    That seems pretty typical of ruby programmers: cram everything into one
    line and call it "clear".
    --
    Posted via http://www.ruby-forum.com/.
    7stud --, May 2, 2008
    #5
  6. Albert Schlef

    Arlen Cuss Guest

    [Note: parts of this message were removed to make it a legal post.]

    Hi,

    On Fri, May 2, 2008 at 7:34 PM, 7stud -- <> wrote:

    > > I've compressed your loop into this:
    > >
    > > (headers.select{ |h| h[:name] == 'content-length' }.first)[:value]

    >
    > That seems pretty typical of ruby programmers: cram everything into one
    > line and call it "clear".
    >


    It's still done in a very neat fashion, and the one that makes the most
    sense from a functional programming point of view.

    Speaking as objectively as possible, it's better than perhaps your proposal
    since it accurately shows what we're doing (`selecting' item hs where the
    [:name] is 'content-length', then picking the `first' and getting the
    [:value]..), and doesn't rely on creating new local variables.

    Arlen
    Arlen Cuss, May 2, 2008
    #6
  7. Albert Schlef

    Todd Benson Guest

    On Fri, May 2, 2008 at 4:34 AM, 7stud -- <> wrote:
    > Sandro Paganotti wrote:
    > > I've compressed your loop into this:
    > >
    > > (headers.select{ |h| h[:name] == 'content-length' }.first)[:value]

    >
    > That seems pretty typical of ruby programmers: cram everything into one
    > line and call it "clear".


    It looks a lot cleaner to me than using 'break'.

    Todd
    Todd Benson, May 2, 2008
    #7
  8. Albert Schlef

    James Gray Guest

    On May 2, 2008, at 5:07 AM, Arlen Cuss wrote:

    > Hi,
    >
    > On Fri, May 2, 2008 at 7:34 PM, 7stud -- <>
    > wrote:
    >
    >>> I've compressed your loop into this:
    >>>
    >>> (headers.select{ |h| h[:name] == 'content-length' }.first)[:value]

    >>
    >> That seems pretty typical of ruby programmers: cram everything into
    >> one
    >> line and call it "clear".
    >>

    >
    > It's still done in a very neat fashion, and the one that makes the
    > most
    > sense from a functional programming point of view.


    Well, the find() iterator makes the most sense, I would say.
    select().first() is just a hand-rolled find() that's slower to run and
    takes more memory, of course. ;)

    The archives should give readers plenty of evidence regarding how much
    attention should be given to 7stud's Ruby opinions though.

    James Edward Gray II
    James Gray, May 2, 2008
    #8
  9. Thanks you all for these snippets.

    However, the snippets you gave me may fail, because there's one detail I
    neglected to mention --as I thought it was obvious from my code:

    The 'content-type' header may be missing. That's because servers don't
    always return it: in '302 redirect' replies there isn't a 'content-type'
    at all.

    The problem with the snippets I was given here is that they
    unconditionaly do [:value] on some expression. When the 'content-type'
    is missing, the expression is 'nil', and since the [] method isn't
    defined for NilClass, the code fails with an exception.

    I know I can change your "some_expression.first[:value]" to
    "(some_expression || {}).first[:value]" and then things would work. OK.
    But I wondered if there's some nifty solution. I don't mind seeing a
    Ruby 1.9 only solution (or a Ruby 2.0 one) --though I'm using 1.8, this
    question is mostly to satisfy my curiosity.
    --
    Posted via http://www.ruby-forum.com/.
    Albert Schlef, May 2, 2008
    #9

  10. > I know I can change your "some_expression.first[:value]" to
    > "(some_expression || {}).first[:value]" and then things would work. OK.


    A typo. I meant "((some_expression).first || {})[:value]".
    --
    Posted via http://www.ruby-forum.com/.
    Albert Schlef, May 2, 2008
    #10
  11. Albert Schlef

    Luc Heinrich Guest

    On 2 mai 08, at 15:05, Albert Schlef wrote:

    > I know I can change your "some_expression.first[:value]" to
    > "(some_expression || {}).first[:value]" and then things would work.
    > OK.
    > But I wondered if there's some nifty solution.


    How about this:

    content_type_header = lambda { |h| h[:name] == 'content-type' }
    content_type = headers.find(&content_type_header).fetch:)value) if
    headers.any?(&content_type_header)

    --
    Luc Heinrich -
    Luc Heinrich, May 2, 2008
    #11
  12. Albert Schlef

    Luc Heinrich Guest

    On 2 mai 08, at 15:31, Luc Heinrich wrote:

    > content_type_header = lambda { |h| h[:name] == 'content-type' }
    > content_type = headers.find(&content_type_header).fetch:)value) if
    > headers.any?(&content_type_header)


    Or, since you can pass a proc to the find method which gets called
    when nothing is found, this:

    content_type = headers.find(lambda {Hash.new}) { |h| h[:name] ==
    'content-type' }.fetch:)value, nil)

    --
    Luc Heinrich -
    Luc Heinrich, May 2, 2008
    #12
  13. * Albert Schlef <> (11:02) schrieb:

    > I have the following array:
    >
    > headers = [
    > { :name => 'user-agent', :value => 'blah blah' },
    > { :name =>'content-type', :value => 'text/html' },
    > { :name => 'pragma', :value => 'no-cache' },
    > { :name =>'content-length', :value => '30' },
    > { :name =>'content-type', :value => 'text/html' },
    > ]
    >
    > Now, I want to extract the :value of the first header of a specific
    > :name. For example, I want to extract the :value of the 'content-type'
    > header.


    That's simply the wrong data structure for the task. So the question is:
    why not use a Hash?

    mfg, simon .... l
    Simon Krahnke, May 2, 2008
    #13
  14. Simon Krahnke wrote:
    > That's simply the wrong data structure for the task.
    > So the question is: why not use a Hash?


    (HTTP servers may return several headers having the same name, so I
    can't use a hash.)

    Luc Heinrich wrote:
    > Or, since you can pass a proc to the find method which
    > gets called when nothing is found, this:
    >
    > content_type = headers.find(lambda {Hash.new}) {
    > ... }.fetch:)value, nil)


    Luc, thanks. But I don't think it's more elegant than ".find { cond } ||
    {}".

    Luc, tell me, why does find() accept this proc parameter? What is it
    useful for? Ok, I understand it calls it if no value found, but... we
    could do "|| expression" instead, couldn't we? Perhaps there is
    something here I miss?
    --
    Posted via http://www.ruby-forum.com/.
    Albert Schlef, May 2, 2008
    #14
  15. Albert Schlef

    Luc Heinrich Guest

    On 2 mai 08, at 16:24, Albert Schlef wrote:

    > Luc, tell me, why does find() accept this proc parameter?


    Because "there's more than one way to do it" :)

    --
    Luc Heinrich -
    Luc Heinrich, May 2, 2008
    #15
  16. Albert Schlef

    Luc Heinrich Guest

    On 2 mai 08, at 16:24, Albert Schlef wrote:

    > (HTTP servers may return several headers having the same name, so I
    > can't use a hash.)


    Well, you could do that:

    headers = {
    'user-agent' => 'blah blah',
    'pragma' => 'no-cache',
    'content-length' => '30',
    'content-type' => ['text/html', 'text/plain']
    }

    --
    Luc Heinrich -
    Luc Heinrich, May 2, 2008
    #16
  17. El Viernes, 2 de Mayo de 2008, Simon Krahnke escribi=F3:
    > That's simply the wrong data structure for the task. So the question is:
    > why not use a Hash?


    Maybe because in some text protocol (as HTTP or SIP) there can be some head=
    ers=20
    appearing more than one time (being completely valid). Also a header name c=
    an=20
    be case insensitive and this requires a extension of the Hash class or=20
    delegation.


    =2D-=20
    I=F1aki Baz Castillo
    Iñaki Baz Castillo, May 2, 2008
    #17
  18. * Iñaki Baz Castillo <> (00:07) schrieb:

    > El Viernes, 2 de Mayo de 2008, Simon Krahnke escribió:
    >> That's simply the wrong data structure for the task. So the question is:
    >> why not use a Hash?

    >
    > Maybe


    Yeah, maybe, or maybe not. That's why I ask.

    > because in some text protocol (as HTTP or SIP) there can be some headers
    > appearing more than one time (being completely valid).


    You can use an Array as the value.

    > Also a header name can be case insensitive and this requires a
    > extension of the Hash class or delegation.


    Or I just use the lower case representation as the key.

    The only case where you just can't use a Hash is when there is an order
    on the elements that can't be reconstructed. But even then I can just
    put the Hash keys in an additional Array.

    mfg, simon .... l
    Simon Krahnke, May 3, 2008
    #18
    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. Replies:
    4
    Views:
    255
  2. Replies:
    0
    Views:
    565
  3. giaro
    Replies:
    17
    Views:
    790
    James Kanze
    Dec 4, 2007
  4. Koos Pol
    Replies:
    10
    Views:
    3,558
    Koos Pol
    May 28, 2008
  5. Ebenezer
    Replies:
    11
    Views:
    1,127
    Ebenezer
    Nov 10, 2008
Loading...

Share This Page