Extracting a value from an array

A

Albert Schlef

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

Jano Svitok

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

Sandro Paganotti

I've compressed your loop into this:

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

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

7stud --

Albert said:
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
 
7

7stud --

Sandro said:
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".
 
A

Arlen Cuss

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

Hi,

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
 
T

Todd Benson

Sandro said:
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
 
J

James Gray

Hi,

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
 
A

Albert Schlef

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

Albert Schlef

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]".
 
L

Luc Heinrich

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

Luc Heinrich

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

Simon Krahnke

* Albert Schlef said:
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
 
A

Albert Schlef

Simon said:
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 said:
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?
 
L

Luc Heinrich

(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']
}
 
I

Iñaki Baz Castillo

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
 
S

Simon Krahnke

* Iñaki Baz Castillo said:
El Viernes, 2 de Mayo de 2008, Simon Krahnke escribió:

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
 

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,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top