Simple array.each do |x| question

Hello, I am new to ruby and cannot understand why this code is not
working:

direction = doc.elements.each("doc:iso_10303_28/uos/IfcCartesianPoint")
{ |element| puts element.attributes["id"] }
# this produces [i1586,i1667,i1915,i2041,i2160]
result = []
direction.each do |z|
  n = direction[z].attribute["id"]
  result << n
end

This gives me the following error: Error: #<TypeError: C:/Program Files
(x86)/Google/Google SketchUp 8/Plugins/examples/one.rb:61:in `[]': can't
convert REXML::Element into Integer>

However when I use the following code, which to me seems to do exactly
the same thing in a less sophisticated manner, it works without issue:

direction = doc.elements.each("doc:iso_10303_28/uos/IfcCartesianPoint")
{ |element| puts element.attributes["id"] }
n=0
result = []
while n != direction.length
z = direction[n].attributes["id"]
result << z
n=n+1
end
#output - > ["i1586", "i1667", "i1915", "i2041", "i2160"]

Any ideas?

Addition info - Ruby 1.8.6.

···

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

Hi

Hello, I am new to ruby and cannot understand why this code is not
working:

direction = doc.elements.each("doc:iso_10303_28/uos/IfcCartesianPoint")
{ |element| puts element.attributes["id"] }
# this produces [i1586,i1667,i1915,i2041,i2160]
result =
direction.each do |z|
n = direction[z].attribute["id"]
result << n
end

The variable z does not contain an index but the object you need. Try

direction.each do |z|
n = z.attribute["id"]
result << n
end

This should also work:

result = direction.map{|z| z.attribute["id"]}

···

2011/4/1 Kyle X. <haebooty@yahoo.com>:

--
Roger Braun
rbraun.net | humoralpathologie.de

This all sounds great but I am a little confused
with what you exactly mean by, "Feel free to swap out
do . . . end with { . . . }."

ruby actually has two syntaxes for blocks:

1)

['hello', 'world', 'goodbye'].each {|word| puts word.upcase}

2)

['hello', 'world', 'goodbye'].each do |word|
  puts word.upcase
end

The general practice is: use parentheses for one liners, and do-end for
multi line blocks.

There is actually one slight difference in effect between the two
syntaxes, but it is not worth mentioning at this point.

···

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

Doh, sorry Chad, just a typo there. Sometimes my mind reads what it
wants to read.

···

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

Roger Braun wrote in post #990270:

Hi

end

The variable z does not contain an index but the object you need. Try

direction.each do |z|
n = z.attribute["id"]
result << n
end

This should also work:

result = direction.map{|z| z.attribute["id"]}

Thanks for the reply; however, both of these produce a new error is
being produced:

Error: #<ArgumentError: C:/Program Files (x86)/Google/Google SketchUp
8/Plugins/examples/one.rb:62:in `attribute': wrong number of arguments
(0 for 1)>

···

2011/4/1 Kyle X. <haebooty@yahoo.com>:

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

As I recall, the only difference is binding precedence -- and I think the
braces bind more tightly(?), but I may have that backwards. It hasn't
come up for me so far; I'll have to look it up to remind myself if it
ever does come up.

···

On Sun, Apr 03, 2011 at 03:58:41AM +0900, 7stud -- wrote:

> This all sounds great but I am a little confused
> with what you exactly mean by, "Feel free to swap out
> do . . . end with { . . . }."

ruby actually has two syntaxes for blocks:

1)

['hello', 'world', 'goodbye'].each {|word| puts word.upcase}

2)

['hello', 'world', 'goodbye'].each do |word|
  puts word.upcase
end

The general practice is: use parentheses for one liners, and do-end for
multi line blocks.

There is actually one slight difference in effect between the two
syntaxes, but it is not worth mentioning at this point.

--
Chad Perrin [ original content licensed OWL: http://owl.apotheon.org ]

Thank you Chris and 7stud, both of your explanations are great and are
very helpful. The way you breakdown the examples seems much more clear
than in the documentation I find elsewhere which, not having a
programing background, can be difficult to digest. Thank you both again
very much for your time and help.

And if your coming from perl, or likewise hate typing quotes and commas
for arrays of strings, the above code can be written like this:

Unfortunately I am not coming from and programming language background
but civil engineering, and for ph.d work in the field apparently you
need to know how to program these days....

···

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

attribute is a method, so try z.attribut('id')

···

2011/4/1 Kyle X. <haebooty@yahoo.com>

Roger Braun wrote in post #990270:
> Hi
>
> 2011/4/1 Kyle X. <haebooty@yahoo.com>:
>> end
> The variable z does not contain an index but the object you need. Try
>
> direction.each do |z|
> n = z.attribute["id"]
> result << n
> end
>
> This should also work:
>
> result = direction.map{|z| z.attribute["id"]}

Thanks for the reply; however, both of these produce a new error is
being produced:

Error: #<ArgumentError: C:/Program Files (x86)/Google/Google SketchUp
8/Plugins/examples/one.rb:62:in `attribute': wrong number of arguments
(0 for 1)>

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

Thank you Chris and 7stud, both of your explanations are great and are
very helpful. The way you breakdown the examples seems much more clear
than in the documentation I find elsewhere which, not having a
programing background, can be difficult to digest. Thank you both again
very much for your time and help.

Did you mean s/Chris/Chad/, or was there some commentary from a Chris
that I missed? If so, I'll go searching for it in archives to see if I
can learn something from Chris.

>
> And if your coming from perl, or likewise hate typing quotes and commas
> for arrays of strings, the above code can be written like this:

Unfortunately I am not coming from and programming language background
but civil engineering, and for ph.d work in the field apparently you
need to know how to program these days....

We all need to start somewhere. Luckily, Ruby makes it pretty easy to
get started with object oriented programming. Best of luck.

···

On Tue, Apr 05, 2011 at 03:55:25AM +0900, Kyle X. wrote:

--
Chad Perrin [ original content licensed OWL: http://owl.apotheon.org ]

Gunther Diemant wrote in post #990275:

attribute is a method, so try z.attribut('id')

That fixed it, thanks very much for your time and help!

···

2011/4/1 Kyle X. <haebooty@yahoo.com>

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

Thank you all for your responses. I have a second part question. I
still cannot get the array.each do || correct, so I have been having to
use these long and unsophisticated while loops:

···

-------------------------------------
n = 0
elements = []
while n != reference1.length
  x =
REXML::XPath.match(doc,"//*[@id='#{reference2[n]}']/Coordinates/IfcLengthMeasure").map
{|element| element.text}
  elements << x
  n=n+1
end

n = 0
result = []
while n != elements.length
  a = 0
    while a != elements[n].length
      x = elements[n][a].to_f
      result << x
      a = a+1
    end
  n=n+1
end
------------------------------------

Any ideas on how to make these more concise by using .each? I have been
trying many different things but cannot make it work. Thank you for
your time.

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

I seem to have misplaced the thread so far, so please forgive me if I'm
asking something redundant.

What is the relationship between reference1 and reference2?

If they are the same, you should be able to do a very simple swap of
terms between your while loop version and an each iterator:

    elements =
    reference2.each do |ref|
      x = REXML::XPath.match(
        doc,
        "//*[@id='#{ref}']/Coordinates/IfcLengthMeasure"
      ).map {|element| element.text }

      elements << x
    end

(adjusted to reduce horizontal sprawl)

If reference1 and reference2 are not the same length, however, this
approach will not work. The lack of descriptive meaning in your variable
names is a bit daunting when trying to figure out the intentions behind
your code.

I'll come back to the second while loop later, if someone else has not
already gotten to it by then.

···

On Sat, Apr 02, 2011 at 04:29:13AM +0900, Kyle X. wrote:

Thank you all for your responses. I have a second part question. I
still cannot get the array.each do || correct, so I have been having to
use these long and unsophisticated while loops:

-------------------------------------
n = 0
elements =
while n != reference1.length
  x =
REXML::XPath.match(doc,"//*[@id='#{reference2[n]}']/Coordinates/IfcLengthMeasure").map
{|element| element.text}
  elements << x
  n=n+1
end

--
Chad Perrin [ original content licensed OWL: http://owl.apotheon.org ]

It occurs to me that you're just going through all the elements of an
array within an array here, in order, such that if you have this array:

    [[0,1,2],[3,4,5],[6,7,8]]

. . . you're just operating the elements as though the entire data
structure was nothing but a flat array:

    [0,1,2,3,4,5,6,7,8]

Keeping that in mind, you could use a basic elements.flatten.each block
instead:

    result =
    elements.flatten.each {|n| result << n.to_f }

You could use a map/collect block instead, which I think is clearer:

    result = elements.flatten.collect {|n| n.to_f }

I find "collect" a more mnemonically helpful term for this operation
given Ruby's block syntax, though "map" works well for me in Perl. In
Ruby, the terms are interchangeable, so if you really want to type less
you could use "map" instead of "collect" here.

It occurs to me that the solution I gave you last time (assuming it
works) could be combined with this one:

    result = reference2.each do |ref|
      REXML::XPath.match(
        doc,
        "//*[@id='#{ref}']/Coordinates/IfcLengthMeasure"
      ).map {|element| element.text }
    end.flatten.map {|n| n.to_f }

Feel free to swap out do . . . end with { . . . } or map with collect as
needed, of course -- or to keep things separate if you think that's
clearer. Ultimately, clarity of intent is probably the most important
factor in deciding how to format your code.

···

On Sat, Apr 02, 2011 at 04:29:13AM +0900, Kyle X. wrote:

Thank you all for your responses. I have a second part question. I
still cannot get the array.each do || correct, so I have been having to
use these long and unsophisticated while loops:

-------------------------------------
n = 0
elements =
while n != reference1.length
  x =
REXML::XPath.match(doc,"//*[@id='#{reference2[n]}']/Coordinates/IfcLengthMeasure").map
{|element| element.text}
  elements << x
  n=n+1
end

n = 0
result =
while n != elements.length
  a = 0
    while a != elements[n].length
      x = elements[n][a].to_f
      result << x
      a = a+1
    end
  n=n+1
end
------------------------------------

--
Chad Perrin [ original content licensed OWL: http://owl.apotheon.org ]

Chad Perrin wrote in post #990462:

{|element| element.text}
  elements << x
  n=n+1
end

I seem to have misplaced the thread so far, so please forgive me if I'm
asking something redundant.

What is the relationship between reference1 and reference2?

I am sorry for not including this. reference1 and reference2 are two
arrays of the same length that contain strings.

If you were to do -
p reference1
#This would be the output->
#["i1671", "i1793", "i1919", "i2045"]

If they are the same, you should be able to do a very simple swap of
terms between your while loop version and an each iterator:

    elements =
    reference2.each do |ref|
      x = REXML::XPath.match(
        doc,
        "//*[@id='#{ref}']/Coordinates/IfcLengthMeasure"
      ).map {|element| element.text }

      elements << x
    end

(adjusted to reduce horizontal sprawl)

If reference1 and reference2 are not the same length, however, this
approach will not work. The lack of descriptive meaning in your
variable
names is a bit daunting when trying to figure out the intentions behind
your code.

I'll come back to the second while loop later, if someone else has not
already gotten to it by then.

Thank you for the response I will try it out.

···

On Sat, Apr 02, 2011 at 04:29:13AM +0900, Kyle X. wrote:

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

Chad Perrin wrote in post #990492:

Chad Perrin wrote in post #990462:
>
> I'll come back to the second while loop later, if someone else has
> not already gotten to it by then.

Thank you for the response I will try it out.

I still haven't gotten around to looking at the second loop. I'm
between
articles right now, and I just saw this response -- and wanted to say
that you should make sure you understand the code I provided rather than
copying it directly and never thinking about why it works (or doesn't
work: I can't swear it is perfect, because I did not test it, which is
something I should have mentioned). If you have any questions about how
the code works (or not), please feel free to ask, and I'll explain to
the
best of my ability.

Thank you again Mr. Perrin, I went over the code you provided and
everything seems clear so far. Being new to Ruby the most confusing
thing for me is just learning the new nomenclature, which with all
things takes some time. I have extracted the principle (from your code)
modified slightly and used them on other similar cases, and have not run
into an error in my testing so far.

Chad Perrin wrote in post #990497:

{|element| element.text}
      result << x
      a = a+1
    end
  n=n+1
end
------------------------------------

It occurs to me that you're just going through all the elements of an
array within an array here, in order, such that if you have this array:

    [[0,1,2],[3,4,5],[6,7,8]]

. . . you're just operating the elements as though the entire data
structure was nothing but a flat array:

    [0,1,2,3,4,5,6,7,8]

Keeping that in mind, you could use a basic elements.flatten.each block
instead:

    result =
    elements.flatten.each {|n| result << n.to_f }

You could use a map/collect block instead, which I think is clearer:

    result = elements.flatten.collect {|n| n.to_f }

So once I flatten the array it would become - > [0,1,2,3,4,5,6,7,8] from
[[0,1,2],[3,4,5],[6,7,8]] correct?

This would then allow me to all in one .to_f, yes? Would this be
possible to do without flattening, or is it not possible with arrays in
an array?

I find "collect" a more mnemonically helpful term for this operation
given Ruby's block syntax, though "map" works well for me in Perl. In
Ruby, the terms are interchangeable, so if you really want to type less
you could use "map" instead of "collect" here.

It occurs to me that the solution I gave you last time (assuming it
works) could be combined with this one:

    result = reference2.each do |ref|
      REXML::XPath.match(
        doc,
        "//*[@id='#{ref}']/Coordinates/IfcLengthMeasure"
      ).map {|element| element.text }
    end.flatten.map {|n| n.to_f }

Feel free to swap out do . . . end with { . . . } or map with collect as
needed, of course -- or to keep things separate if you think that's
clearer. Ultimately, clarity of intent is probably the most important
factor in deciding how to format your code.

This all sounds great but I am a little confused with what you exactly
mean by, "Feel free to swap out do . . . end with { . . . }."

Thanks for the great information. I kind of figure that there would be
built in commands for the same essential function as I am trying to get
across in my unsophisticated code. Usually when I try to look for the
particular built in function it will take me hours to find (or not
find), and I end up getting discouraged and writing something that is
ugly, unclear and long to just get the job done. It is great to
actually be able to converse with such kind and knowledgeable people as
the people here. Thank you again for all your time and help.

···

On Sat, Apr 02, 2011 at 05:31:26AM +0900, Kyle X. wrote:
On Sat, Apr 02, 2011 at 04:29:13AM +0900, Kyle X. wrote:

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

I still haven't gotten around to looking at the second loop. I'm between
articles right now, and I just saw this response -- and wanted to say
that you should make sure you understand the code I provided rather than
copying it directly and never thinking about why it works (or doesn't
work: I can't swear it is perfect, because I did not test it, which is
something I should have mentioned). If you have any questions about how
the code works (or not), please feel free to ask, and I'll explain to the
best of my ability.

···

On Sat, Apr 02, 2011 at 05:31:26AM +0900, Kyle X. wrote:

Chad Perrin wrote in post #990462:
>
> I'll come back to the second while loop later, if someone else has
> not already gotten to it by then.

Thank you for the response I will try it out.

--
Chad Perrin [ original content licensed OWL: http://owl.apotheon.org ]

Kyle X. wrote in post #990532:

So once I flatten the array it would become - > [0,1,2,3,4,5,6,7,8] from
[[0,1,2],[3,4,5],[6,7,8]] correct?

Yes, and you can always test that. If you use:

  p some_array

ruby will output the array structure.

This would then allow me to all in one .to_f, yes? Would this be
possible to do without flattening, or is it not possible with arrays in
an array?

You wouldn't want to flatten in that case. Instead you can use some
nested map() calls:

data = [[0,1,2],[3,4,5],[6,7,8]]

transformed_data = data.map do |arr|
   arr.map do |int|
     int.to_f
   end
end

p transformed_data

--output:--
[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]

map() sends each element of the data array to the block parameter arr.
Because each element of the data array is a sub-array, I named the block
parameter 'arr' to indicate that. Now that you have a reference to the
sub-array inside the outer block, you can apply another map() to the
subarray to do the actual transformation. The inner map() block returns
a new array that has transformed one of the data sub-arrays to floats.
And the outer map stuffs each of those returned-transformed-arrays into
a new array--producing the final result.

···

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

Kyle X. wrote in post #990532:
>
> This would then allow me to all in one .to_f, yes? Would this be
> possible to do without flattening, or is it not possible with arrays in
> an array?

You wouldn't want to flatten in that case. Instead you can use some
nested map() calls:

data = [[0,1,2],[3,4,5],[6,7,8]]

transformed_data = data.map do |arr|
   arr.map do |int|
     int.to_f
   end
end

. . . or, for a one-line version:

    transformed_data = data.map {|arr| arr.map {|int| int.to_f } }

I don't recommend trying to fit everything into one line, of course.
Spreading things out a bit can make them easier to understand later,
sometimes. You should (pretty much) always judge how you organize your
code by how easily another programmer will be able to understand it, even
if that other programmer is you six months later after having forgotten
what you did when you first wrote it.

map() sends each element of the data array to the block parameter arr.
Because each element of the data array is a sub-array, I named the block
parameter 'arr' to indicate that. Now that you have a reference to the
sub-array inside the outer block, you can apply another map() to the
subarray to do the actual transformation. The inner map() block returns
a new array that has transformed one of the data sub-arrays to floats.
And the outer map stuffs each of those returned-transformed-arrays into
a new array--producing the final result.

What he said. You can read more about it (and other Ruby Array methods)
using either ri or Google (of course). For instance, part of what you
get from `ri Array` is:

    Includes:

···

On Sun, Apr 03, 2011 at 03:32:23AM +0900, 7stud -- wrote:
    ---------
         Enumerable(all?, any?, collect, count, cycle, detect, drop,
         drop_while, each_cons, each_slice, each_with_index, entries,
         enum_cons, enum_slice, enum_with_index, find, find_all, find_index,
         first, grep, group_by, include?, inject, inject, map, max, max_by,
         member?, min, min_by, minmax, minmax_by, none?, one?, partition,
         reduce, reject, reverse_each, select, sort, sort_by, take,
         take_while, to_a, to_set, zip)

. . . and you can use ri to find out about each of them, such as by
asking it about map with `ri Array.map`. In Google, I find that doing a
search for something like "ruby array" (without quotes) basically always
gives me the documentation for the appropriate class as the first hit.
For "ruby array", I get this as the first hit:

    class Array - RDoc Documentation

Sometimes, solving a problem in writing Ruby code is as simple as
checking out method lists like those provided by ri and Google to remind
myself about a method I forgot I had at my disposal.

--
Chad Perrin [ original content licensed OWL: http://owl.apotheon.org ]