Jump to content
gsc

How to sort list with a letter-number elements

Recommended Posts

gsc

Hi,

 

I want to sort a list based on the following  elements:

 

Input: ((S1 (x y z)) (P2 (x y z)) (S2 (x y z)) (P1 (x y z)) (P3 (x y z)) (S3 (x y z)))

Result Output: ((S1 (x y z)) (S2 (x y z)) (S3 (x y z)) (P1 (x y z)) (P2 (x y z)) (P3 (x y z)))

 

So element (S1 (x y z)) needs to be the first element of the list, followed by all other S number elements in ascending order.
after the S number elements the P number elements must be sorted the same way.

Also the list does not contain a fixed amount of S and P elements, so the number sequence for each letter may vary

 

Anyone an idea how to do this?

 

 

 

Share this post


Link to post
Share on other sites
hanhphuc
3 hours ago, gsc said:

 

Input: ((S1 (x y z)) (P2 (x y z)) (S2 (x y z)) (P1 (x y z)) (P3 (x y z)) (S3 (x y z)))

Result Output: ((S1 (x y z)) (S2 (x y z)) (S3 (x y z)) (P1 (x y z)) (P2 (x y z)) (P3 (x y z)))

 

 

just dummy idea not tested, but if letter ascending should be P=first S=after

 

(mapcar 'read (acad_strlsort (mapcar 'vl-princ-to-string lst)))

 

Share this post


Link to post
Share on other sites
BIGAL

The vl-sort supports multi level sorting so can have say multi S1's and then second sort on X Y Z. This should make the P occur 1st.

 

; sorts on 1st two items
(vl-sort lst
	 '(lambda (a b)
	    (cond
	      ((< (car a) (car b)))
	      ((= (car a) (car b)) (< (cadr a) (cadr b)))
	    )
	  )
)

;x only
(setq lst (vl-sort lst '(lambda (x y) (< (car x)(car y)))))

(vl-sort '(3 2 1 20 98.32 16 0 20 -2.345) '<)
(-2.345 0 1 2 3 16 20 98.32) only 1 20

(setq test '(87 6.54 20 1 8.88 20))   ; contains two 20's
(mapcar 'cdr (vl-sort (mapcar '(lambda (x) (cons 1 x)) test) '(lambda (y z) (< (cdr y) (cdr z)))))
returns:
(1 6.54 8.88 20 20 87); both 20's still there

 

 

 

 

Share this post


Link to post
Share on other sites
gsc

I know but S stands for Starboard Side and P for Port Side.
The S and P elements are (hidden) attributes in a block (which is a ship) representing an anchor location at the ship.
S1 and P1 for instance are Tag Names and also the default Values of the attribute objects.
The ship (in block mode) is always pointing to the North with the 0,0 origin at the stern (not necessarily in the middle)
 

With a sub routine, the WCS coordinates of the anchors are stored in a LIST for later use.

The insertion point of the attributes is actually the start point of an anchor line which is drawn

However the List order must be according to this table, which is used as an input to read in (the known) angle, length anchor wire data and possible mid line buoy distance.

But the table is also placed into the drawing, filled with the (calculated) anchor coordinates after setting them out with the (relative) angle and distance (length anchor wire).

Each ship/barge we use may have a different amount of Starboard Side and Port Side anchors, so S1 and P1 are always there, but the amount (= the number) may vary per ship/barge.
Now it would be easier if I changed the order (P1 first row) in the excel sheet, but my internal clients are a bit autistic 😉 ...they want to see the S anchors first in the table


image.png.e49487b317a1fb66f36b442abefcbdb6.png

 

For now I have changed the default values of the hidden attributes to A1 (for Starboard) and B1 (for Port), because this way the function of hanhphuc works.

But it would be nicer if the result list could be manipulated to the correct order:

 

(mapcar 'read (acad_strlsort (mapcar 'vl-princ-to-string lst)))

Result Output: ((P1 (x y z)) (P2 (x y z)) (P3 (x y z)) (S1 (x y z)) (S2 (x y z)) (S3 (x y z)))

Desired output: ((S1 (x y z)) (S2 (x y z)) (S3 (x y z)) (P1 (x y z)) (P2 (x y z)) (P3 (x y z)))

 

So after the first code split the result list between P and S elements and shift all S elements left to first position.
Is that possible?
 

 

Edited by gsc

Share this post


Link to post
Share on other sites
hanhphuc
1 hour ago, gsc said:

 

For now I have changed the default values of the hidden attributes to A1 (for Starboard) and B1 (for Port), because this way the function of hanhphuc works.

But it would be nicer if the result list could be manipulated to the correct order:

 


(mapcar 'read (acad_strlsort (mapcar 'vl-princ-to-string lst)))

Result Output: ((P1 (x y z)) (P2 (x y z)) (P3 (x y z)) (S1 (x y z)) (S2 (x y z)) (S3 (x y z)))

Desired output: ((S1 (x y z)) (S2 (x y z)) (S3 (x y z)) (P1 (x y z)) (P2 (x y z)) (P3 (x y z)))

 

So after the first code split the result list between P and S elements and shift all S elements left to first position.
Is that possible?
 

 

if your list pattern is simple, vl-string-translate 
(mapcar '(lambda(x) (read (vl-string-translate "PS" "SP" x))) (acad_strlsort (mapcar 'vl-princ-to-string lst)))

 

FWIW..  sorting string is way different

(acad_strlsort  '("P3" "P1" "P2" "P123" "P11"))

; ("P1" "P11" "P123" "P2" "P3")

numeric sort (vl-sort '(1 11 123 2 3) '<)

;'(1 2 3 11 123)

 

 

 

 

 

 

 

 

 

 

Share this post


Link to post
Share on other sites
BIGAL

An easier way may be look at P or S then add to correct list Plst or Slst sort both of them individually then join them into final lst the desired output. Note below the sort is greater than saves a reverse list

 

(setq lstp (list  (list "P2" (list 4 4 6))  (list "P1" (list 1 2 3)) (list "P3" (list 0 1 2 ))))
(setq lsts (list (list "S1" (list 1 2 3)) (list "S3" (list 4 5 6)) (list "S2" (list 7 8 9))))
(setq lstp (vl-sort lstp '(lambda (x y) (> (car x)(car y)))))
(setq lsts (vl-sort lsts '(lambda (x y) (> (car x)(car y)))))
(setq lst '())
(foreach x lstp
(setq lst (cons x lst))
)
(foreach x lsts
(setq lst (cons x lst))
)

 

Share this post


Link to post
Share on other sites
gsc

Pfff....still struggling with this sub routine. Let me be more specific.

The goal of this sub routine is to iterate through all present attributes in a block, and list their value and wcs coordinate.

 

(defun getatt ( blk / enx )
    (if (and (setq blk (entnext blk)) (= "ATTRIB" (cdr (assoc 0 (setq enx (entget blk))))))
       (setq lst
            (cons
                (cons
                    (cdr (assoc 1 enx)) ; attrib value
                    (cdr (assoc 10 enx)) ; wcs coordinate
                )
                (getatt blk)
            )
        )
    )
)

 

The result list is:

((S2 239.209 265.528 0.0) (P1 259.56 341.311 0.0) (P2 256.249 342.843 0.0) (S1 271.666 335.713 0.0) (P3 223.721 272.691 0.0))

 

I deliberately created the attributes in a dis-order because users who will create these attribute blocks may do the same.

But the list should be ordered like this for later use:

((S1 271.666 335.713 0.0) (S2 239.209 265.528 0.0) (P1 259.56 341.311 0.0) (P2 256.249 342.843 0.0) (P3 223.721 272.691 0.0))

S items first followed by P items, because I want the coordinates to be listed in a certain order
The amount of S and P items may vary per attribute block

How do I do this efficiently?

 

 

Share this post


Link to post
Share on other sites
dlanorh

If lst contains your list then

 

(setq lst (vl-sort lst '(lambda (x y) (if (= (substr (car x) 1 1) (substr (car y) 1 1)) (< (atoi (substr (car x) 2)) (atoi (substr (car y) 2))) (> (substr (car x) 1 1) (substr (car y) 1 1))))))

 

As S1, S2 ..P# are strings (attribute tags) and the (cadr) of each item in the list is itself a list (coordinates)

Edited by dlanorh

Share this post


Link to post
Share on other sites
gsc

Wow that was quick!...this is working...thanx man

Share this post


Link to post
Share on other sites
dlanorh
32 minutes ago, gsc said:

Wow that was quick!

 

Nope. It was a late reply to your first post. :lol:

Share this post


Link to post
Share on other sites
BIGAL

Nice dlanorh maybe atof rather than atoi as example has decimals.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...