Author Topic: sort items into a selection set.  (Read 22149 times)

0 Members and 1 Guest are viewing this topic.

daron

  • Guest
sort items into a selection set.
« on: November 24, 2003, 04:30:01 PM »
Let's say you need to get a selection set of a group of text objects all on a specific layer and you need to sort these objects by their text string number, i.e. "1" "2" "3"... All text objects will only have numbers in the string. What would be a quick way to sort the objects into a list of selection sets by this number from smallest to largest? I'm muddling through some ideas, but the one I've got takes a great deal of time during the sorted selection phase.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
sort items into a selection set.
« Reply #1 on: November 24, 2003, 05:26:33 PM »
Well, lets see, you want to sort a selection set based on the text value of DXF 1.

I don't see a simple solution right off the top of my head, but, how about this.

Extract the text value of each text entity and append them one to another in a list... i.e. ("23" "31" "3" "42" "18" .......) ignoring the entity that they came from. At the same time create a list using the numeric values of the text....i.e. (23 31 3 42 18 .......)

then foreach N in the numbered list.....
(setq X -1)
(foreach N numbered_list (setq X (max X N)))

Now you have the maximum number of digits in the highest number in the list, lets say 14361

Now you take each string in the text_list and prepend the correct number of "0" to the beginning of each string.

(foreach n text_list (while (<(strlen n)(strlen (rtos X 2 0))) (setq n(strcat "0" n)))
(setq new_list (append new_list n))
)

Use  acad_strlsort to sort the new_list

(setq sorted_list (acad_strlsort new_list))

Now lets order the entities...

.....
to be continued......
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

daron

  • Guest
sort items into a selection set.
« Reply #2 on: November 24, 2003, 05:45:08 PM »
Okay, this is what I currently have. sslist is a list of vla-objects that are text. I probably should not start out with vla-objects, but this is a routine I wrote about 6 months ago and am trying to improve. Currently, the portion that slows everything down is the part that re-sorts every object back into the list, not the sorting of the numbers. I probably should have shown this in the beginning, but it is so big, I wanted to see what others could come up with.
Code: [Select]
(defun selection-set-sorter (sslist) ;(setq sslist vla-list)
     (setq vl-string-list nil)
     (foreach string sslist
 (setq vl-string-list
   (append vl-string-list
   (list (atoi (vla-get-textstring string)))
   )
 )
     )
     (setq vl-sort-list (mapcar 'itoa
      (vl-sort vl-string-list '<))
  vla-list-sorted nil
     )
     (foreach item vl-sort-list
 (setq vla-list-sorted
   (append vla-list-sorted
   (list (ssget "x"
(list (cons 1 item))
 )
   )
   )
 )
     )
     (setq index 0
  vla-list-new nil
  index (1- (vl-list-length vla-list-sorted)
)
     )
     (repeat (vl-list-length vla-list-sorted)
 (setq vla-list-new
   (cons
(vlax-ename->vla-object
     (ssname
  (nth index
vla-list-sorted
  )
  0
     )
)
vla-list-new
   )
index (1- index)
 )
     )
)

Mark

  • Custom Title
  • Seagull
  • Posts: 28753
sort items into a selection set.
« Reply #3 on: November 24, 2003, 07:01:41 PM »
Code: [Select]
(defun ss->vla-list (ss lst)
  (cond ((ssname ss 0)
    (setq lst (append (list
      (vlax-ename->vla-object (ssname ss 0))) lst))
     (ss->vla-list (ssdel (ssname ss 0) ss) lst)); 1st cond
    ((null (ssname ss 0)) lst); 2nd cond
    ); cond
  )


(setq ss (ssget '((0 . "TExT"))))

(setq obj-lst (SS->VLA-LIST ss nil))

(setq obj-lst-s
   (vl-sort obj-lst
      '(lambda (x y)
         (<
           (atof (vla-get-TextString x))
           (atof (vla-get-TextString y))
           )
         )
       )
      )
TheSwamp.org  (serving the CAD community since 2003)

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
sort items into a selection set.
« Reply #4 on: November 24, 2003, 09:53:06 PM »
Daron,

Here is my version. Can't tell how fast it is?
Not what you were looking for I sure but I thought I'd try to do anyway.


Mark, you show off :lol:


Code: [Select]
(defun c:srt (/ x cnt item NewList RawList)
  (setq ss (ssget '((0 . "TEXT"))))
  (setq x   0
cnt (sslength ss)
  )
  (cond
    ((/= ss nil)
     (repeat cnt
       (setq RawList (append
      RawList
      (list (cons (ssname ss x)
  (cdr (assoc 1 (entget (ssname ss x))))
    )
      )
    )
    x     (1+ x)
       )
     )
     ;sort list
     (setq NewList
  (vl-sort RawList '(lambda (E1 E2) (< (cdr E1) (cdr E2))))
  x   0
  NewSs   (ssadd)
     )
     (foreach item NewList
       (setq NewSs (ssadd (car item) NewSs))
     )

    ) ; end cond 1
  )


  (princ)
)

I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

JohnK

  • Administrator
  • Seagull
  • Posts: 10603
sort items into a selection set.
« Reply #5 on: November 24, 2003, 11:15:52 PM »
WOW Mark.  Im in awe! That is just perdy!
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

SMadsen

  • Guest
sort items into a selection set.
« Reply #6 on: November 25, 2003, 06:21:22 AM »
Daron,
I'm at a complete loss as to what you're trying to do. The routine you posted returns an integer and what it is doing otherwise is a complete mystery :D

Is it CAB's routine you're after, i.e. returning (or "should be" returning) a selection set with items sorted according to strings representing numbers? Or do you want it as a list of vla-objects sorted in the same manner like Mark's solution?

CAB, your routine doesn't return anything, but leaves a global sset to be picked up. That might not be the best way to do it. But, if I read Daron's original request correctly I think that is perhaps what comes closest. I would put a couple of features into it, though. Such as checking if text holds a number, adding the layer functionality that Daron talked about and returning the new sset instead of leaving a global variable:

Code: [Select]
(defun selsort (ss lyr / elst a ent num? newSs)
  (defun getss (l)
    (ssget "X" (list '(0 . "TEXT")
                 (cons 8 (cond (l)("*")))))
  )
  (cond ((or ss (setq ss (getss lyr)))
         (setq a 0 newSs (ssadd))
         (repeat (sslength ss)
           (setq ent (ssname ss a))
           (if (numberp (setq num? (read (cdr (assoc 1 (entget ent))))))
             (setq elst (cons (cons ent num?) elst))
           )
           (setq a (1+ a))
         )
         (setq elst (vl-sort elst (function (lambda (x y) (< (cdr x) (cdr y))))))
         (foreach n elst
           (ssadd (car n) newSs)
         )
        )
  )
)


Daron, if your intention is to return a list of sorted vla-objects then you could replace the last FOREACH with something like

Code: [Select]
(mapcar (function (lambda (n)(vlax-ename->vla-object (car n)))) elst)

.. or, for a sorted list of enames, a simple thing like (mapcar 'car elst)

Mark

  • Custom Title
  • Seagull
  • Posts: 28753
sort items into a selection set.
« Reply #7 on: November 25, 2003, 07:51:58 AM »
Maybe this? Weeds out none numeric strings and returns a list ov vla-objects.
Code: [Select]

;; selection set to vla-object list
(defun ss->vla-list (ss lst)
  (cond ((ssname ss 0)
    (setq lst (append (list
      (vlax-ename->vla-object (ssname ss 0))) lst))
     (ss->vla-list (ssdel (ssname ss 0) ss) lst)); 1st cond
    ((null (ssname ss 0)) lst); 2nd cond
    ); cond
  )

;; test each item in a list of integers for a value
;; of between 48-57 (ascii for 0-9)
;; returns T if the list contins ONLY values 48-57
(defun is_eq_only_num (lst)
  (vl-every
    '(lambda (i) (and (> i 47)(< i 58)))
    lst
    )
  )

;; remove those vla-objects in the list(vl-lst) that are
;; NOT numbers or contain ANY letters. i.e. "4square"
;; would be removed were "456" would not.
(defun ret_only_num (vl-lst / nlst)
  (setq nlst
        (vl-remove-if-not
          '(lambda (x)
             (is_eq_only_num (vl-string->list (vla-get-textstring x)))
             )
          vl-lst
          )
        )
  (sort_lst_by_num nlst)
  )

;; sort a list of vla-objects that equal ONLY numbers
(defun sort_lst_by_num (vl-lst / vl-lst-s)
  (setq vl-lst-s
        (vl-sort vl-lst
                 '(lambda (x y)
                    (<
                      (atof (vla-get-TextString x))
                      (atof (vla-get-TextString y))
                      )
                    )
                 )
        )
  )

;; testing function
(defun funs-test ( / ss obj-lst sorted-lst)
  (if (setq ss (ssget '((0 . "TEXT"))))
    (if (setq obj-lst (SS->VLA-LIST ss nil))
      (setq sorted-lst (ret_only_num obj-lst))
      )
    )
  sorted-lst
  )
TheSwamp.org  (serving the CAD community since 2003)

Mark

  • Custom Title
  • Seagull
  • Posts: 28753
sort items into a selection set.
« Reply #8 on: November 25, 2003, 08:05:19 AM »
You've heard the expression "you learn something new everyday", I got an early start! Looking at SMadsen's getss function above. I saw this (cons 8 (cond (l)("*"))). Thanks Mr. Madsen, that is excellent.
TheSwamp.org  (serving the CAD community since 2003)

SMadsen

  • Guest
sort items into a selection set.
« Reply #9 on: November 25, 2003, 08:27:38 AM »
Just another way to say (if this this that)

I like your VL-EVERY stuff. It's not vl-every day one gets to see that function in use.
But what if the number is negative? I know Daron said integers only but what if it's a real?

Mark

  • Custom Title
  • Seagull
  • Posts: 28753
sort items into a selection set.
« Reply #10 on: November 25, 2003, 08:36:38 AM »
>But what if the number is negative?
>but what if it's a real?
Excellent question. Let's ask D-A-R-O-N.....
TheSwamp.org  (serving the CAD community since 2003)

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
sort items into a selection set.
« Reply #11 on: November 25, 2003, 08:52:33 AM »
OK, lets see if I understand the routine correctly.






Code: [Select]
;; Sort function by SMadsen
;; function called with sel set and layer name
;;  note, values ss or lay may be nil
(defun selsort (ss lyr / elst a ent num? newSs)
  ;; sub function here
  (defun getss (l) ; if sel set nil, go get one
    (ssget "X" ; use all of drawing with the following filter in a list
  (list '(0 . "TEXT") ; get only TEXT objects
(cons 8        ;(8 . layername)
      (cond (l)   ; COND test for nil layername, returns layer NAME
    ("*") ; OR returns the wildcard character to get ALL Layers
      )
)
  )
    )
  )


  ;;;  main function starts here
  (cond  ; ss has sel set already OR go get a sel set
    ((or ss (setq ss (getss lyr))) ; if ss nil and can't find any on layer do nothing
     (setq a 0  ; set pointer to 0
  newSs (ssadd) ; create an empty sel set
     )
     (repeat (sslength ss) ; loop for each item in the sel set
       (setq ent (ssname ss a)) ; get the name of each item in the set one at a time
       (if (numberp  ; test num? to see if it is a number, skip (setq elst if not number
    (setq num?  ; store the word in var num?
   (read  ; get the first word if text has a space character
     (cdr  ; get the TEXT only
(assoc 1  ; get the TEXT doted pair
      (entget ent) ; get entity data
      )))))
(setq elst ; build a list of name and text
(cons   ; add doted pair list to elst ((ename . "25") (ename . "40"))
 (cons ent num?) ; create a doted pair of name & number (ename . "25")
    elst))
       )
       (setq a (1+ a)) ; increment pointer
     )
     (setq elst ; rebuild list of sorted name & text
   ;;;  you got me, don't understand yet how this works
   (vl-sort elst (function (lambda (x y) (< (cdr x) (cdr y)))))
     )
     (foreach n elst ; step through newly sorted doted pair list, n = (ename . "25")
       (ssadd  ; add ename to end of new sel set newSs
(car n)   ; get ename only from doted pair
    newSs)
     )
    )
  )
  ;;  "foreach" returns the last value & "ssadd" returns the sel set after each add
  ;;   therfore the sorted sel set is returned when the function is complete
  ;;  EXCEPT if no ss was passed AND no ss was found on the layer
  ;;  THEN nil is returned

 
)

;; routine to call function
(defun C:srt ()
  ;;  cal selsort with layer only so All text on that layer will be analized
  (setq NewSs (selsort nil "zDtl xLight 3"))
  (princ)

)
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

SMadsen

  • Guest
sort items into a selection set.
« Reply #12 on: November 25, 2003, 09:05:50 AM »
Ah, a code analysis. That's cool, CAB!

In (if (numberp (setq num? .... , it uses READ to test for a number. It's the result from READ that gets dotted with the ename ((ename . 25)(ename . 40)...), not the string. This means that if a string holds a unit or some other text, it is still being regarded as a number, e.g. "25 mm" would be stored as (ename . 25). Thought that might be a cool feature but it all depends on the specific use, of course.

You say that you don't get how the LAMBDA works? Hmm, you did it the same way in your routine :) What part of it is unclear?

All your other comments are correct. Nicely spotted that it can be called with nil arguments :)  E.g. (selsort nil nil) grabs all TEXTs and sort those that contains a number.

daron

  • Guest
sort items into a selection set.
« Reply #13 on: November 25, 2003, 09:17:09 AM »
Well, since you're asking, I'll elaborate. Most of you guys here deal with land. Well, I deal with taking the land you guys set up and ripping it apart into individual lots. Each object I'm selecting here is a lot number. I've never seen a negative lot number, a real number or one's with letters contained in them, so I'm not too worried about getting lettered, negative or real numbered strings.

Here's the entire job that is happening with this routine. Certain things are set up before hand, like separating each individual lot and setting objects on specific layers. The layer of the lot numbers is, LOTNO2. Don't ask. Enter the routine. I call it Super Write Block. It runs a page setup, sets the limits of the page, inserts a titleblock specific to certain city requirements and selects each object on LOTNO2, sorts them, zooms to the center of each lot, one at a time. It then allows the user to select the lot and other objects, moves them to the middle of the titleblock, allows the user to move the objects to fit better. When the user hits enter, the routine moves the lot number to the upper right corner of the titleblock, moves a predefined north arrow into a block where it belongs, then reads a txt file, looking for a match of the lot number with whatever line it finds, then reads the next line as the lot address and puts it in place where it belongs and many other things. When all is finished, it should plot it and then write block it out using the lot number as the drawing name, i.e 01.dwg. To keep all the drawings sorted, I use C for any drawing < 9 and > 100. For 100 > I use L. Haven't had to worry about 1000, yet.

As far as this function goes, it takes all the objects that have been selected and sorts them based on their lot number. I think the problem I'm having with speed has to do with converting ename's to vla-objects, then sorting, then re-selecting ename's by their sort order and re-converting them to vla-objects. I think I know what I need to do, but when you spend all day looking at something, it's nice to get some fresh ideas from others. Thank you all for your input and ideas. I like them all.

daron

  • Guest
sort items into a selection set.
« Reply #14 on: November 25, 2003, 09:19:38 AM »
Code: [Select]
(cons 8        ;(8 . layername)
             (cond (l)   ; COND test for nil layername, returns layer NAME
              ("*") ; OR returns the wildcard character to get ALL Layers
             )
       )
You remember that OPTION argument question I asked a while ago? This is what I would consider an optional argument. That is cool.