Extracting a value from an array

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

On Fri, May 2, 2008 at 11:02 AM, Albert S. [email protected]
wrote:

Now, I want to extract the :value of the first header of a specific
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]

I’ve compressed your loop into this:

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

Albert S. 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

Sandro P. 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”.

On Fri, May 2, 2008 at 4:34 AM, 7stud – [email protected] wrote:

Sandro P. 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

Hi,

On Fri, May 2, 2008 at 7:34 PM, 7stud – [email protected] 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

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.

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

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. :wink:

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

James Edward G. II

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]”.

On 2 mai 08, at 15:05, Albert S. 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)

On 2 mai 08, at 15:31, Luc H. 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)

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 K. 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 H. 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?

On 2 mai 08, at 16:24, Albert S. wrote:

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

Because “there’s more than one way to do it” :slight_smile:

On 2 mai 08, at 16:24, Albert S. 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’]
}

El Viernes, 2 de Mayo de 2008, Simon K.
escribió:> 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
headers
appearing more than one time (being completely valid). Also a header
name can
be case insensitive and this requires a extension of the Hash class or
delegation.

El Viernes, 2 de Mayo de 2008, Simon K. 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