# Ruby array variable

**URL:** <https://forums.sketchup.com/t/ruby-array-variable/45897>\
**Category:** Ruby API\
**Tags:** sketchup, method, array\
**Created:** [June 2, 2017, 7:00pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897 "2017-06-02T19:00:12Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 2, 2017, 7:00pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/1 "2017-06-02T19:00:12Z")

</div>

I’m a beginner in Ruby.  
I think my problem is my variable (list\_pieces) whose values are not memorized by my Definition.  
How do I do it? An expert can help me please?  
Here is my code, which works with dynamic components whose sketchup file is saved.

> New code below.

---

<div class="post-metadata">

**Author:** ![eneroth3](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/eneroth3/32/1222_2.png) [@eneroth3](https://forums.sketchup.com/u/eneroth3)\
**Post date:** [June 3, 2017, 8:02pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/2 "2017-06-03T20:02:25Z")

</div>

First of all, if you write in English it’s easier for everyone to understand and you’ll get more replies.

Regarding the Array it seems like you expect the method examiner\_pieces to have access to the variable defined outside of it. In Ruby variables doesn’t work like this. Either you can pass the variable as an argument to examiner\_pieces and the method will have access to the same Array object or you can make it a class or class instance variable.

Some general comments on the code: you don’t need parenthesis for methods that doesn’t take any arguments (e.g. “attribute\_dictionaries()”) but for arguments that do take arguments it’s much recommended to have parenthesis around them to make the code easier to read (e.g. “UI.savepanel “Enregistrer rapport LDP”, modelPath, filename.chomp(”.skp")+“.csv”").

---

<div class="post-metadata">

**Author:** ![gkernan](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@gkernan](https://forums.sketchup.com/u/gkernan)\
**Post date:** [June 3, 2017, 8:50pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/3 "2017-06-03T20:50:29Z")

</div>

Personally I find easier reading when using empty parenthesis for methods such as attribute\_dictionaries().

It is probably because of my background which is heavy in C / C++. Regardless - empty parenthesis tells me that it is a method and not a reference to a class member. For the same reason when I define methods that do not have parameters I also use empty parenthesis. I also like to use them for if statements.

Ultimately it is a stylistic choice.

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 4, 2017, 1:47am UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/4 "2017-06-04T01:47:28Z")

</div>

Ok i have to pass the variable. I’ll try. But can someone tell me the way by giving examples? TY

---

<div class="post-metadata">

**Author:** ![Aerilius](https://avatars.discourse-cdn.com/v4/letter/a/e19b73/32.png) [@Aerilius](https://forums.sketchup.com/u/Aerilius)\
**Post date:** [June 4, 2017, 11:25am UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/5 "2017-06-04T11:25:41Z")

</div>

### 1. There is only one return value.

So to make `infos_piece` return a list, you have to add elements to a temporary list and return that.

```auto
def infos_piece(pi)
    results = []
    pi.each{ |defs|
        # …
            results << tPiece
    }
    return results
end

```

* * *

### 2. Attaching a returned list to another list.

Also take care of the meaning of `<<`:  
It inserts at the back (`list.push(element)`). To add another list to list, use `list.concat(other_list)`.

```auto
liste_pieces = []
liste_pieces.concat(infos_piece(entites))

```

* * *

### 3. Passing a list as argument

Alternatively, you can pass the original list as (mutable) argument and insert elements into it instead of creating every time new temporary lists.

```auto
def ajoute_infos_piece(liste, pi)
    pi.each{ |defs|
        # …
            liste << tPiece
    }
end
# …
liste_pieces = []
ajoute_infos_piece(liste_pieces, entites)

```

* * *

### 4. Proper recursion end conditions

Recursion can perform a lot of work since it branches itself (exponential!). So you want it to stop somewhen and not cause a stack overflow.  
Your recursion (`examiner_Pieces(en)`) only ends when it encounters an empty list (because then `each` does nothing and does not do more recursive calls). However, you assume everything contained in a list is iterable. Another abort condition is an element that is not iterable.

I would prefer to structure recursion that way:

```auto
def examiner_pieces(liste, ent)
    # Aborts if ent is nil or does not fulfill this condition
    if ent && ent.name == 'dynamic_attributes'
        ajoute_infos_piece(liste, ent)
        # Recurses only further if ent is iterable.
        if ent.respond_to?(:each)
            ent.each{ |en|
                examiner_pieces(liste, en)
            }
        end
    end
end

```

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 4, 2017, 10:44pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/6 "2017-06-04T22:44:47Z")

</div>

I try this right now, TY Aerilius.

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 5, 2017, 5:09am UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/7 "2017-06-05T05:09:51Z")

</div>

I have tried everything according to my knowledge, nothing works. I’ll try again tomorrow.

---

<div class="post-metadata">

**Author:** ![Aerilius](https://avatars.discourse-cdn.com/v4/letter/a/e19b73/32.png) [@Aerilius](https://forums.sketchup.com/u/Aerilius)\
**Post date:** [June 6, 2017, 2:06pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/9 "2017-06-06T14:06:08Z")

</div>

It’s not a mistake in the code, but in your expectation: In your model, the `model.entities` collection contains only a single entity, so the code only outputs one line of the attributes of this entity.

If you want to traverse also entities nested in groups/components, you need to call your function again with the entities contained in the group/component. You can do this with recursion (see the code below). But there is one conceptional problem: when a component has multiple instances (n), you would visit the contained entities n times. If you don’t want that, you either have to keep track of all component definitions that you have already visited, or you traverse over `model.definitions` (linearly instead of recursion).

```auto
﻿def infos_piece(entities)
    lines = []
    entities.each{ |entity|
        dicts = entity.attribute_dictionaries()
        next if dicts == nil
        # Read attributes from this entity
        infos = []
        dicts.each{ |dict|
            next if dict.name != 'dynamic_attributes'
            puts '', "dictionary: #{dict.name}, definition #{entity.name}"
            dict.each_pair{ |key, value|
                infos << sprintf("%s=%s",key,value)
            }
        }
        lines << infos.join(", ") unless infos.empty?
        # If the entity can contain other entities, recurse over the contained entities
        if entity.is_a?(Sketchup::ComponentInstance) || entity.is_a?(Sketchup::Group)
          lines.concat( infos_piece(entity.definition.entities) )
        end
    }
    return lines
end

```

* * *

```auto
           iPiece[0] = '' #Supprimer premier caractère (,)

```

Don’t do that! You are assuming a comma was added at the beginning of the string, but the comma was not added at the beginning, but after `iPiece`. Instead of adding and removing characters, it is safer not to add the comma unless you need it.

```auto
                iPiece += "," if iPiece.length != 0
                iPiece += sprintf("%s=%s",key,value)

```

or much better by **joining a list** with the character you want only to appear inbetween elements `list.join(",")`. In programming you should never use assumptions, you must be able to prove that what you do is correct.

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 1:58pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/10 "2017-06-07T13:58:05Z")

</div>

**Code UPDATED**  
I have simplify the code. And this work fine right now. (Code below)  
My def “attrib\_piece” is not the problem.

**My problem it’s my def “lister\_pieces”.**  
**I need it to get ALL entities (also components in component) in the model.**

My code list only the main components.  
`_has_movetool_behaviors=0.0,_lengthunits=INCHES,_name=CubesGroup,textgroup=group text example`

**Someone can show me how to get that?**

> ```
> def attrib_piece(defs)
> iPiece = "" #Infos piece
> dicts = defs.attribute_dictionaries
> if dicts != nil
> dicts.each{ |dict|
> if dict.name == 'dynamic_attributes'
> dict.each{ |key, value|
> iPiece = iPiece + "," + key.to_s + '=' + value.to_s
> }
> iPiece[0] = '' #Supprimer premier caractère (,)
> end
> }
> end
> return iPiece
> end
> 
> ```

> ```
> def lister_pieces(ents)
> lpieces = Array.new
> ents.each{ |ent|    
> lpieces << attrib_piece(ent)        
> }
> return lpieces
> end
> #-----
> #START
> #-----
> mod = Sketchup.active_model
> ents = mod.active_entities
> 
> ```

> ```
> les_pieces = Array.new
> les_pieces.concat(lister_pieces(ents))
> 
> ```

> ```
> #Write file
> modelPath = Sketchup.active_model.path
> filename = File.basename(modelPath, '.csv')
> if filename == ""
> filename = "Sans titre"
> end
> save_path = UI.savepanel "Enregistrer rapport CSV", modelPath, filename.chomp(".skp")+".csv"
> if save_path != nil
> File.open(save_path, "w+") do |f|
> les_pieces.each {|element|f.puts(element)}
> end
> end
> 
> ```

---

<div class="post-metadata">

**Author:** ![Aerilius](https://avatars.discourse-cdn.com/v4/letter/a/e19b73/32.png) [@Aerilius](https://forums.sketchup.com/u/Aerilius)\
**Post date:** [June 7, 2017, 2:18pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/11 "2017-06-07T14:18:39Z")

</div>

Please don’t take it amiss, but my previous exactly solves this task:  
A SketchUp model is a [tree](https://fr.wikipedia.org/wiki/Parcours_d%27arbre), not a [sequential list](https://fr.wikipedia.org/wiki/Liste_(informatique)). You only get top-level components if you don’t dive down into the tree (see `entity.definition.entities`).

And please fix `iPiece[0] = ''`, it breaks your resulting string.

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 2:26pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/12 "2017-06-07T14:26:39Z")

</div>

I know I’m noob in ruby… 😕  
**Then my def “lister\_pieces” will never be able the do it?**

**I mean… I have to get all pieces by (entity.definition.entities) ?**

In my case…  
iPiece[0] = ‘’ , really get out the first “,” of my line. Really it’s at the beginning of the line! 😄  
(Because the first time, iPiece is empty)

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 2:54pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/13 "2017-06-07T14:54:47Z")

</div>

> [@Aerilius](#):
>
> def infos\_piece(entities)  
> lines =   
> entities.each{ |entity|  
> dicts = entity.attribute\_dictionaries()  
> next if dicts == nil  
> # Read attributes from this entity  
> infos =   
> dicts.each{ |dict|  
> next if dict.name != ‘dynamic\_attributes’  
> puts ‘’, “dictionary: #{dict.name}, definition #{entity.name}”  
> dict.each\_pair{ |key, value|  
> infos \<\< sprintf(“%s=%s”,key,value)  
> }  
> }  
> lines \<\< infos.join(", ") unless infos.empty?  
> # If the entity can contain other entities, recurse over the contained entities  
> if entity.is\_a?(Sketchup::ComponentInstance) || entity.is\_a?(Sketchup::Group)  
> lines.concat( infos\_piece(entity.definition.entities) )  
> end  
> }  
> return lines  
> end

I try your def infos\_piece and it give me only 1 result, why?

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 3:18pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/14 "2017-06-07T15:18:48Z")

</div>

This give me only 1 result 😕

```
def lister_pieces(ents)
    lpieces = Array.new
    ents.each{ |ent|    
        lpieces << attrib_piece(ent)
        if ent.respond_to?(:each)
            ent.each{ |en|
                lpieces.concat(lister_pieces(en))
            }
        end
    }
    return lpieces
end

```

---

<div class="post-metadata">

**Author:** ![john\_drivenupthewall](https://avatars.discourse-cdn.com/v4/letter/j/c67d28/32.png) [@john\_drivenupthewall](https://forums.sketchup.com/u/john_drivenupthewall)\
**Post date:** [June 7, 2017, 3:50pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/15 "2017-06-07T15:50:02Z")

</div>

```ruby
> lpieces = Array.new
[]
> lpieces << 'item'
["item"]
> lpieces = Array.new # this will empty the array each time it runs
[]
> lpieces << 'item'
["item"]
> lpieces = Array.new if lpieces.empty? # this will not

> lpieces << 'item'
["item", "item"]
> lpieces = Array.new if lpieces.empty? # this will not

> lpieces << 'item'
["item", "item", "item"]

```

john

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 4:04pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/16 "2017-06-07T16:04:32Z")

</div>

> [@john\_drivenupthewall](#):
>
> lpieces = Array.new if lpieces.empty?

It give me…  
undefined method ‘empty?’ for nil:NilClass

---

<div class="post-metadata">

**Author:** ![john\_drivenupthewall](https://avatars.discourse-cdn.com/v4/letter/j/c67d28/32.png) [@john\_drivenupthewall](https://forums.sketchup.com/u/john_drivenupthewall)\
**Post date:** [June 7, 2017, 5:05pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/17 "2017-06-07T17:05:15Z")

</div>

sorry that will fail on the first one, before it exists…

```ruby
 lpieces = Array.new unless lpieces.is_a?(Array)

```

should cover all tries…

john

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 5:36pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/18 "2017-06-07T17:36:29Z")

</div>

No problemo. But still give me only the first component! ☹

---

<div class="post-metadata">

**Author:** ![john\_drivenupthewall](https://avatars.discourse-cdn.com/v4/letter/j/c67d28/32.png) [@john\_drivenupthewall](https://forums.sketchup.com/u/john_drivenupthewall)\
**Post date:** [June 7, 2017, 5:53pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/19 "2017-06-07T17:53:36Z")

</div>

post your full code and an example model…

it’s hard to guess all the missing detail…

john

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 6:04pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/20 "2017-06-07T18:04:40Z")

</div>

here John…

> **[Dropbox - File Deleted](https://www.dropbox.com/sh/np3m6leialahety/AACk4JK7LCPiK7nDR1njm0y4a?dl=0)**
>
> Dropbox is a free service that lets you bring your photos, docs, and videos anywhere and share them easily. Never email yourself a file again!

---

<div class="post-metadata">

**Author:** ![House](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/house/32/33601_2.png) [@House](https://forums.sketchup.com/u/House)\
**Post date:** [June 7, 2017, 6:19pm UTC](https://forums.sketchup.com/t/ruby-array-variable/45897/21 "2017-06-07T18:19:01Z")

</div>

In my report file « Component Cubes.csv » I need to see…

```
_has_movetool_behaviors=0.0,_lengthunits=INCHES,_name=CubesGroup,textgroup=group text example
_has_movetool_behaviors=0.0,_iscollapsed=false,_lengthunits=INCHES,_name=CubeA,integerten=10,stringcubea=CubeA
_has_movetool_behaviors=0.0,_iscollapsed=false,_lengthunits=INCHES,_name=CubeB,integertwenty=20,stringcubeb=CubeB
_has_movetool_behaviors=0.0,_lengthunits=INCHES,_name=CubeC,integerthirty=30,stringcubec=CubeC
_has_movetool_behaviors=0.0,_lengthunits=INCHES,_name=TinyCubeA,attribtesta=tCubeA
_has_movetool_behaviors=0.0,attribtestb=tCubeB
_has_movetool_behaviors=0.0,attribtestc=tCubeC
_has_movetool_behaviors=0.0,attribtestd=tCubeD
_has_movetool_behaviors=0.0,attribteste=tCubeE
_has_movetool_behaviors=0.0,attribtestf=tCubeF
_has_movetool_behaviors=0.0,attribtestg=tCubeG
_has_movetool_behaviors=0.0,attribtesth=tCubeH

```

🙂

[Next page](https://forums.sketchup.com/t/ruby-array-variable/45897.md?page=2)
