Selection Set Processing
There are several ways to iterate over all objects in a selection set, some methods are more intuitive, others are more efficient.
Below I will provide some examples to demonstrate a variety of different methods and I'll try my best to give a brief explanation on how each method works and the efficiency of its operation.
In all of the following examples, the user will be prompted to make a selection of objects and the example program will iterate over the selection set, printing the entity type of each entity, (or object name of the VLA-Object in the case of the Visual LISP example).
Many of the examples have been expanded to use several variables for simplicity and readability and could subsequently be shortened by nesting expressions, but that is not the intention of this tutorial.
Method 1: while
(defun c:test1 ( / e i n s x ) (if (setq s (ssget)) (progn (setq i 0 n (sslength s) ) (while (< i n) (setq e (ssname s i) x (cdr (assoc 0 (entget e))) i (1+ i) ) (print x) ) ) ) (princ) )
The above function utilises a while loop to iterate over the selection set. A counter variable i is initialised at 0, and a limiting variable n is assigned the size of the selection set, i.e. the number of items in the set.
The test condition for the while loop will evaluate to T (true) providing the counter is less than the number of items in the set.
The counter variable is then sequentially incremented within the while loop, ensuring termination of the loop when all entities have been processed.
Note that all selection sets have a zero-based index, and hence the first entity in the selection resides at index 0, with the last entity at index sslength - 1 (i.e. the number of items in the selection set minus one).
Method 1a: Reverse while
(defun c:test1a ( / e i s x ) (if (setq s (ssget)) (progn (setq i (1- (sslength s))) (while (<= 0 i) (setq e (ssname s i) x (cdr (assoc 0 (entget e))) i (1- i) ) (print x) ) ) ) (princ) )
This example uses the same logic as the preceding example, except the counter variable is initialised at the last index in the set, and decremented within the loop until it reaches zero.
This approach removes the need for the 'restricting variable' as required by the previous example.
Method 2: repeat
(defun c:test2 ( / e i s x ) (if (setq s (ssget)) (progn (setq i 0) (repeat (sslength s) (setq e (ssname s i) x (cdr (assoc 0 (entget e))) i (1+ i) ) (print x) ) ) ) (princ) )
The above function also uses a counter variable i which is incremented within the repeat loop to iterate over the set, however, since the number of items in the set can be determined before the loop is executed (using sslength), this more efficient repeat loop may be utilised to iterate over the set a number of times equal to the number of items in the set.
Why more efficient? Because there is no longer a test condition to be evaluated on each pass of the loop.
Method 2a: Reverse repeat
(defun c:test2a ( / e i s x ) (if (setq s (ssget)) (repeat (setq i (sslength s)) (setq e (ssname s (setq i (1- i))) x (cdr (assoc 0 (entget e))) ) (print x) ) ) (princ) )
Analogous to the reversed example of the while loop (Method 1a), this rather more concise (but perhaps less readable) example will initialise the counter variable i at the number of items in the set, whilst using the return of this setq expression to supply the repeat function with the number of iterations for the loop.
The counter variable is then immediately decremented within the loop (so as to start the processing at the (sslength - 1)th entity), and again, the return of this setq expression is supplied to the ssname function.
This example has a negligible difference in efficiency to the previous example, but is more concise.
Method 3: ssnamex
(defun c:test3 ( / s ) (if (setq s (ssget)) (foreach e (ssnamex s) (if (= 'ename (type (cadr e))) (print (cdr (assoc 0 (entget (cadr e))))) ) ) ) (princ) )
The ssnamex function can be used to return information about how a selection set was collected. This information also includes all of the entity names in the selection.
The above example will iterate over the list returned by ssnamex and process the entities contained therein.
However, do not be deceived into thinking that this method is efficient simply because it looks more concise than the previous examples...
The ssnamex function is a process intensive function and is slow to evaluate, furthermore, the foreach loop may iterate a number of times greater than the number of items in the set as the ssnamex function includes additional information about any window selections (or other selection methods) that the user may have used.
Method 4: ssdel
(defun c:test4 ( / e s ) (if (setq s (ssget)) (while (setq e (ssname s 0)) (print (cdr (assoc 0 (entget e)))) (ssdel e s) ) ) (princ) )
This method iterates over a selection set by sequentially removing the first entity from the set and exploiting the inherent behaviour of the selection set.
A selection set may be viewed as an array of entities. Each entity occupies a position, or index, in the array, with the first entity occupying index 0.
When an entity is removed from the selection set, so as not to have a 'gap' in the array, all entities occupying the positions above the removed entity are shifted down to occupying the lower positions, thus filling the 'gap' created by the removed entity.
In this way, there will always be an entity occupying index 0 for as long as the selection set is not empty.
However, the process of continually shifting the entities to new indexes in the selection set is unnecessary and largely inefficient and so this method should be avoided when processing selection sets containing many objects.
Method 4a: Reverse ssdel
(defun c:test4a ( / e i s ) (if (setq s (ssget)) (progn (setq i (sslength s)) (while (setq e (ssname s (setq i (1- i)))) (print (cdr (assoc 0 (entget e)))) (ssdel e s) ) ) ) (princ) )
Similar to the above technique of using the ssdel function to sequentially remove the first entity from the selection set until no entities remain (Method 4), this method will remove the entity at the (sslength - 1)th index in the selection set, therefore avoiding the inefficiency introduced by shifting each entity to a lower index.
However, although the termination of the while loop in this example relies on the selection set becoming empty after all entities have been processed, Method 1a and Method 2a both demonstrate that removing an entity from the set is not required in order to process the selection set in this manner, and hence these methods are comparatively more efficient than the use of ssdel.
Method 5: Visual LISP ActiveX
(defun c:test5 ( / s ) (if (ssget) (progn (vlax-for o (setq s (vla-get-activeselectionset (vla-get-activedocument (vlax-get-acad-object)))) (print (vla-get-objectname o)) ) (vla-delete s) ) ) (princ) ) (vl-load-com) (princ)
This method is useful when the program needs to operate on the VLA-Object representations of the entities in the selection, and avoids the need to use the vlax-ename->vla-object function to convert each entity to its equivalent VLA-Object representation.
The method retrieves the ActiveSelectionSet Collection from the SelectionSets Collection in the drawing and iterate over the VLA-Objects found therein.
Note however, that when using this method, the VLA SelectionSet Object should be deleted from the SelectionSets Collection after use, as there is a limit to the number of SelectionSets permitted in this Collection.