Here is a simple method to find out if the definition with the name “Heather” is present in the selection:
mod = Sketchup.active_model
sel = mod.selection
sel.grep(Sketchup::ComponentInstance).map(&:definition).map(&:name).include?("Heather")
Instead of that :
presence = 0
mod = Sketchup.active_model
sel = mod.selection
sel.grep(Sketchup::ComponentInstance) do |i|
next unless i.definition.name.include?("Heather")
presence += 1
end
presence == 0 ? false : true
I would like to do the same thing with the value of a dynamic attribute:
mod = Sketchup.active_model
sel = mod.selection
sel.grep(Sketchup::ComponentInstance).map(&:definition).map(&:attribute_dictionaries["dynamic_attributes"])
I don’t know how to create an array of all values because (&:values) didn’t seem to work.
Do you have an idea how to do it?
Any tricks to avoid digging through an array with the “do” method are welcome.
You must not use the #attribute_dictionaries method this way because it returns nil if an entity’s dictionaries collection is empty. When this is so (an empty dictionaries collection,) and nil is returned, an attempt to call #[] on nil will raise a NoMethodError exception for NilClass as this class has no #[] method.
This means that extension code must always test for a “truthy” result when calling #attribute_dictionaries method.
If any instance (or it’s definition) does not have any dictionaries, the result is nil and calling #[] on nil raises a NoMethodError for NilClass.
So, you must use #select instead of #map to ensure only definitions with dictionaries are selected.
mod = Sketchup.active_model
sel = mod.selection
definitions = sel.grep(Sketchup::ComponentInstance).map(&:definition)
those_with_dicts = definitions.select(&:attribute_dictionaries)
dynamic_definitions = those_with_dicts.select(&:attribute_dictionaries['dynamic_attributes')
Now find a dynamic definition with attribute named “Heather”:
has_heather_key = dynamic_definitions.find { |dyna|
dyna.attribute_dictionaries['dynamic_attributes'].keys.include?('Heather')
}
# has_heather_key is nil if not found
Now find a dynamic definition with any attribute value of “Heather”:
has_heather_value = dynamic_definitions.find { |dyna|
dyna.attribute_dictionaries['dynamic_attributes'].values.include?('Heather')
}
# has_heather_value is nil if not found
You don’t need to have the extra @val line as the result of the previous line would also return the result.
And, what is returned is not the value but a Boolean true or false whether it exists.
Using a literal string argument with the insertion of an interpolated reference is frivolous.
Ie, the reference value should be a String object. If not the interpolation will call #to_s upon it.
So it would be simpler to just do:
collection.include?(value.to_s)
Well, you should try for readability and maintainability’s sake, to keep your code lines to 80 characters max.
In order for us to read what you posted, we must scroll horizontally, and that makes your code harder to understand.
Yes, reference assignment takes time, but if the method is not going to be within a loop running many times, then a few intermediate references won’t hurt much. We are talking milliseconds to assign a reference.
I probably have said this many times now, in this and the DC category.
For dynamic attributes, you always check the instance first (for components and nested dynamic groups) and if not a group, then you check the definition second.
DC_DICT ||= 'dynamic_attributes'
def has_dc_attribute?(inst, key, value)
# Check the instance
check = inst.get_attribute(DC_DICT, key)
if !check.nil?
# inst had attribute key
check == value
else
# inst did not have attribute key
definition = inst.definition
# Dynamic groups always have dynamic attributes in instance dictionary:
return false if definition.group?
# Check the component's definition:
check = definition.get_attribute(DC_DICT, key)
!check.nil? && check == value
end
end
Inside the block we have access to the boolean but not outside!
As dynamic attributes added manually on components are automatically attached to instances and definitions, we can do this:
DC ||= "dynamic_attributes"
def include_value_in_dynamic_attribut?( i, value )
i.select(&:attribute_dictionaries).find{ |d|
@boolean = d.attribute_dictionaries[DC].values.include?(value.to_s)
}
end
@boolean = false
mod = Sketchup.active_model
sel = mod.selection
i = sel.grep(Sketchup::ComponentInstance) + sel.grep(Sketchup::Group)
include_value_in_dynamic_attribut?( i, "Heather" )
@boolean
Excellent I take note!
I understand but what matters most to me is to publish the code as it is written in my rb file.
Especially since adapting it to the constraints of the forum will change my logic which is to reduce useless variables as much as possible.
Precisely if, the code will be called in loops or it will be executed several times.
As I said, storing information in intermediate variables increases the number of lines unnecessarily and slows down the code.
I want to optimize my way of coding as much as possible.
Classify in a array all the values of the “heather” attribute using “(&:keys)” then check once if the value sought is found.
This will allow in a single search to obtain a boolean even if hundreds of groups or components are in the selection.
Note on your example:
If components or groups are in the selection, we get an array of Boolean.
If components and groups are in the selection, we get an array of boolean, instances and nil.
For this it is essential to give complete examples without omitting the beginning to check the operation of your code.
No! I’m not asking you to do my job but to publish executable examples with a simple “copy/paste” to help beginners who will go through this.
It is better to write “if check” instead of “if !check.nil?” which requires two conversions makes the code difficult to read.
Note on adding attributes:
When an attribute is added manually the component instances and definitions are attached by the dictionary, which makes the definition.get_attribute test unnecessary.
However if the attributes are added with a Ruby method, your reasoning is correct!
Conclusion :
This research is not really useful to me, I just want to see how an experienced coder can optimize their code.