How to delete the same instances with simple code

#I  want to delete the instances with the same definition. The code is as follows:
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
path = Sketchup.find_support_file "panel.skp","components/"
def_list = mod.definitions
com_def = def_list.load path
trans1 = Geom::Transformation.rotation [0,0,0],[0,0,1a],0.degrees
trans2 = Geom::Transformation.rotation [0,0,0],[-1,0,0],180.degrees
trans3 = Geom::Transformation.rotation [0,0,0],[0,0,1],270.degrees
trans4 = Geom::Transformation.rotation [0,0,0],[1,0,0],270.degrees

inst1 = ent.add_instance com_def,trans1
inst2 = ent.add_instance com_def,trans2
inst3 = ent.add_instance com_def,trans3
inst4 = ent.add_instance com_def,trans4
#It I want to delete these instances, I maybe to use the followed codes:
ent.erase_entities inst1
ent.erase_entities inst2
ent.erase_entities inst3
ent.erase_entities inst4

Is there a simple way to delete the instances with the same definition.

Best,
Stephen

ent.erase_entities inst1
ent.erase_entities inst2
ent.erase_entities inst3
ent.erase_entities inst4

… can be simplified to …

ent.erase_entities( inst1, inst2, inst3, inst4 )

… or …

ent.erase_entities( [inst1, inst2, inst3, inst4] )
ent.erase_entities(
  com_def.instances.select {|inst| ent.include?(inst) } 
)

… will erase ALL of the definition’s instances that are within the ent entities collection.

But if you just wish to delete ALL of com_def’s instances, no matter what entities collection they are a member of (nested or top level) …

com_def.instances.dup.each {|inst| inst.erase! }

This will preserve com_def in the model’s definition list.

But if you will no longer need com_def, then let SketchUp (2018 or higher) delete all the instances for you …

def_list.remove( com_def )

Mr. DanRathbun , many thanks!

1 Like

If I have a array whose data is relevant with every instance, can these instances with the same definition also be defined with a array? It will be better to manage the information of the instances, such as shading.

You can attach an attribute dictionary to either the definition object or any of the instance objects of the definition. An array can be saved as a value of an attribute in a dictionary.

But be aware that the model database is a shared space and attribute dictionary names also need to be unique like your extension module namespace names. A good pattern to follow is to name your extension’s attribute dictionaries using a concatenation of your toplevel namespace module name and the extension submodule name.
Ie, at the top of your extension submodule …

  KEY ||= Module.nesting[0].name.gsub('::','_')

… and then you can use this key as your dictionary name and for saving extension setting using Sketchup.write_default and reading them back using Sketchup.read_default methods.

See:

If I save all definition instances in the current view to an SKP file, then when I open the SKP file again, can I use Ruby to know these definition instances?

The current view has nothing to do with attribute dictionaries.

Please fix your above question. It isn’t proper English and makes no sense.
Please use a translator like Google Translate to make a sensible English question.

If you want to find component instances of a certain definition when you again open the model, then …

DICT_NAME ||= 'AddSummSun_SolarApplicationSimulation'

model = Sketchup.active_model
dlist = model.definitions
cdef = dlist.find {|cdef| cdef.attribute_dictionary(DICT_NAME) }
# cdef will be nil if not found
instances = cdef ? cdef.instances : []

You can add other tests to the find method’s block. For example perhaps you have several definitions that have your extension’s attribute dictionary, and you need to also filter by the definition name …

cdef = dlist.find { |cdef|
  cdef.attribute_dictionary(DICT_NAME) &&
  cdef.name.start_with?('Window')
}

Thanks, I have fixed the question with the help of Google Translator.

Dear DanRathbun,

I don’t quite understand the code in the first paragraph.

DICT_NAME ||= 'AddSummSun_SolarApplicationSimulation'
......

How to connect the macro definition DICT_NAME with the actual definition which is such as follows:

mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
path = Sketchup.find_support_file "panel.skp","components/"
dlist = mod.definitions
com_def = dlist.load path

It is a constant reference assignment that references a String.
The example statement creates the reference using abbreviated assignment.

The constant reference DICT_NAME would be used as the name of your extension’s attribute dictionaries that you attach to the model, any of it’s collections, or any of it’s entities.

# Create the dictionary:
dict = com_def.attribute_dictionary(DICT_NAME, true) 

# Add data to a definition's attribute dictionary:
dict[data_field]= data_value

# Or if you KNOW the dictionary ALREADY exists ...
com_def.set_attribute(dict, data_field, data_value)

As I said above, see the Sketchup::Entity class for all the other attribute dictionary access methods.

Give an small example of this data array. (The code will need an iterator loop to step through the array and assign attributes to definitions and/or instances.)

Dear DanRathbun,

In my PV application, first I will lay out the PV panel and saved to a SKP file.
When I load the SKP file next time, maybe I need to delete these PV pamel.
With your suggestion , I write the followed code,

DICT_NAME || = 'AddSummSun_SolarApplicationSimulation'
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection
path = Sketchup.find_support_file "panel.skp", "components"
dlist = mod.definitions
dict = dlist.attribute_dictionary(DICT_NAME, true) 
dict['test'] = 123
com_def = dlist.load path
#size and number of PV panels
length =1.64.m
width = 0.86.m
a = 0.8.m
num = 15
# lay out PV panels 
num.times do |i|
   tran = Geom::Transformation.translation [(width+a)*i,0,0]
   inst1 = ent.add_instance com_def ,tran
end
# catch the instances with the dictionary 
cdef = dlist.find {|cdef| cdef.attribute_dictionary(DICT_NAME) }
p cdef
instances1 = cdef ? cdef.instances : []
p instances1
# cdef will be nil if not found

With the above code , I can lay out the panels ,but failed to catch the instances with the dictionary. The cdef and the instances are Nil when I print them. Therefore , I can’t remove these instances.
Can you give me more suggestion. Many thanks!

(1) Please use spaces around operators.

(2) You are attaching the dictionary to the definitions list (com_def) and NOT the definition itself (test_panel).

Identify the DefinitionList as dlist or deflist, instead of a singular reference like com_def, and you may not be so confused.

Dear DanRathbun,

Many thanks for your help. I have solved this problem.

1 Like

Dear DanRathbun,

The instances’ delete function with attribute_dictionary is effective when I use in common situations. But when I put them into a procedure, the delete method does not play a role, the code is as follows:

DICT_NAME ||= 'AddSummSun_SolarApplicationSimulation'
mod = Sketchup.active_model # Open model
ent = mod.entities # All entities in model
sel = mod.selection # Current selection
path = Sketchup.find_support_file "panel.skp", "components"
dlist = mod.definitions
com_def = dlist.load path
dict1 = com_def.attribute_dictionary(DICT_NAME,true) 
dict1['test'] = 123
# The followed procedure is to delete the existed layout panels, then re-layout.
def del_relayout(length,width,a,num)
# The followed code is not efficient to find the last layout panels. Why?
cdef = dlist.find {|cdef| cdef.attribute_dictionary(DICT_NAME) }
# cdef will be nil if not found
instances = cdef ? cdef.instances : []
ent.erase_entities( instances )
# The followed code is to test another method deleting existed panels,it is useful.
p "Delete before--" + com_def.instances.count.to_s
com_def.instances.dup.each {|inst| inst.erase! }
p "Delete later--"+ com_def.instances.count.to_s
p com_def.instances.count
# Relayout the panels 
num.times do |i|
   tran = Geom::Transformation.translation [(width+a)*i,0,0]
   inst1 = ent.add_instance com_def,tran
end
p "After deploy--"+com_def.instances.count.to_s
end
# Call the procedure
del_relayout(1.64.m,0.86.m,0.8.m,2)

(1) ALL of your code must evaluate within your toplevel unique namespace module, and preferably each extension in a submodule of your namespace module.

(2) Do NOT use global variables ! Use local variables within methods.
Pass references into methods or use module or instance variables if you need them to be persistent.

(3) Use proper 2 space indentation for Ruby code.

(4) In Ruby method names are all lowercase …

def del_relayout(length,width,a,num)

(5) Use spaces around operators. (=, +, etc.)

(6) Always use the model.active_entities …

ents = mod.active_entities

(7) This code is problematic. …

number2 = instances.count
number2.times do |i|
  ents.erase_entities instances[i]
end

Sketchup::Entities#erase_entities takes an array as an argument, so you might possibly do this …

ents.erase_entities( instances )

BUT, how do you know that all of the instances are a member of the ents collection ?
You don’t and cannot know where the user puts the instances.

Look back at the post above where I showed you safe ways to delete the instances.

Dear DanRathbun,

Thanks for your help. I have canceled the use of global variables in the procedure. Current problem is as follows: when I want to re-layout the PV panels , first I need to find the layout PV panels in the skp file saved last time, but I can’t successfully to find them, I don’t know why this happened , followed are the test codes:

DICT_NAME ||= 'AddSummSun_SolarApplicationSimulation'
mod = Sketchup.active_model # Open model
ent = mod.active_entities # All entities in model
sel = mod.selection # Current selection
dlist = mod.definitions
# Try to find the instances owing the attribute dictionary, and then delete them 
cdef = dlist.find {|cdef| cdef.attribute_dictionary(DICT_NAME) }
instances = cdef ? cdef.instances : []
ent.erase_entities(instances)
# re-layout PV panels, these panels were all attributed with the dictionary name.
path = Sketchup.find_support_file "panel_1.skp", "components"
com_def = dlist.load path
dict1 = com_def.attribute_dictionary(DICT_NAME,true) 
dict1['test'] = 123
# Number and size of PV modules
length = 1.64.m
width = 0.86.m
a = 0.8.m
num = 7
# re-layout PV modules
num.times do |i|
   tran = Geom::Transformation.translation [(width+a)*i,0,0]
   inst1 = ent.add_instance com_def,tran
end
#  Test to find these panels owing the dictionary name after re-layout 
cdef = dlist.find {|cdef| cdef.attribute_dictionary(DICT_NAME)}
instances1 = cdef ? cdef.instances : []
p instances1
p "----------"

Followed is the demonstration of layout PV modules.
Lay_out.skp (190.0 KB)

I NEVER suggested anyone to ever not use a method (aka procedure) !

REREAD what I wrote above.

Until you follow advice and good coding practice I am no longer getting involved.

Dear DanRathun,

I am really appreciated for your help. Sorry for my non-standard coding practice and not too deep understanding for using of RUBY API. I have checked and modified the codes carefully.Can you still give us some advice? Thanks!