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

···

--
Posted via http://www.ruby-forum.com/.

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

or even better:

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

···

On Fri, May 2, 2008 at 11:02 AM, Albert Schlef <albertschlef@gmail.com> 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.)

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 <albertschlef@gmail.com> 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!

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/\.

* Albert Schlef <albertschlef@gmail.com> (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

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/\.

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 Krahnke escribió:

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

--
Iñaki Baz Castillo

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

···

On Fri, May 2, 2008 at 7:34 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

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

Todd

···

On Fri, May 2, 2008 at 4:34 AM, 7stud -- <bbxx789_05ss@yahoo.com> 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".

* Iñaki Baz Castillo <ibc@aliax.net> (00:07) schrieb:

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

···

El Viernes, 2 de Mayo de 2008, Simon Krahnke escribió:

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

···

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

Hi,

On Fri, May 2, 2008 at 7:34 PM, 7stud -- <bbxx789_05ss@yahoo.com> > 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.

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

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/\.

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

--
Luc Heinrich - luc@honk-honk.com

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)

···

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)

--
Luc Heinrich - luc@honk-honk.com

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/\.

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

···

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

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

--
Luc Heinrich - luc@honk-honk.com

Well, you could do that:

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

···

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

--
Luc Heinrich - luc@honk-honk.com