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.

Hi

2011/4/1 Kyle X. [email protected]:

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

Roger B. wrote in post #990270:

Hi

2011/4/1 Kyle X. [email protected]:
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)>

Gunther D. wrote in post #990275:

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

2011/4/1 Kyle X. [email protected]

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

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

2011/4/1 Kyle X. [email protected]

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

{|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?

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.

Chad P. wrote in post #990462:

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

{|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?

Oh I am very sorry, that was a typo and reference1 was meant to be
reference2. As a side note reference2 is and arrays that contain
strings.

If you were to do -
p reference2
#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, it works great!

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

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

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

{|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 }

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.

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
sub-array to do the actual transformation. The inner map sends each
integer element of the sub-array to the block. Finally, the inner map()
returns a new array which contains the transformed data. The outer map
then stuffs each of those returned-transformed-sub-arrays into
a new array, producing the final result.

If the nested map()'s are too confusing, you can just do this:

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

data.each do |arr| #each() sends each sub-array to this block

new_arr = arr.map do |int|
int.to_f
end

transformed_data << new_arr
end

p transformed_data

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

As you can see from the output, all map() does is save you the trouble
of having to stuff the new values into an array yourself. Of course,
there is also the map!() method, which will change the original array in
place. So, for instance, if you array were very large, and you didn’t
want to maintain two arrays in memory, you could use map!().

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:

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

[‘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 worrying about at this point. (If you must
know it’s precedence – the parens block binds tighter than a do-end
block. How precedence applies to blocks may seem mysterious, but in
rare cases it can be an issue.)

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:

%w[hello world goodbye].each {|word| puts word.upcase}

On Sun, Apr 03, 2011 at 03:32:23AM +0900, 7stud – wrote:

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

http://www.ruby-doc.org/core/classes/Array.html

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 P. wrote in post #990492:

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

Chad P. 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 P. wrote in post #990497:

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

{|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 Sun, Apr 03, 2011 at 03:58:41AM +0900, 7stud – wrote:

[‘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.

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 Tue, Apr 05, 2011 at 03:55:25AM +0900, Kyle X. wrote:

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.

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

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 a programming language background
but civil engineering, and for ph.d work in the field apparently you
need to know how to program these days…