gsc Posted June 30, 2020 Share Posted June 30, 2020 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? Quote Link to comment Share on other sites More sharing options...
hanhphuc Posted June 30, 2020 Share Posted June 30, 2020 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))) Quote Link to comment Share on other sites More sharing options...
BIGAL Posted July 1, 2020 Share Posted July 1, 2020 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 Quote Link to comment Share on other sites More sharing options...
gsc Posted July 1, 2020 Author Share Posted July 1, 2020 (edited) 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 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 July 1, 2020 by gsc Quote Link to comment Share on other sites More sharing options...
hanhphuc Posted July 1, 2020 Share Posted July 1, 2020 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) Quote Link to comment Share on other sites More sharing options...
BIGAL Posted July 2, 2020 Share Posted July 2, 2020 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)) ) Quote Link to comment Share on other sites More sharing options...
gsc Posted July 2, 2020 Author Share Posted July 2, 2020 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? Quote Link to comment Share on other sites More sharing options...
dlanorh Posted July 2, 2020 Share Posted July 2, 2020 (edited) 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 July 2, 2020 by dlanorh Quote Link to comment Share on other sites More sharing options...
gsc Posted July 2, 2020 Author Share Posted July 2, 2020 Wow that was quick!...this is working...thanx man Quote Link to comment Share on other sites More sharing options...
dlanorh Posted July 2, 2020 Share Posted July 2, 2020 32 minutes ago, gsc said: Wow that was quick! Nope. It was a late reply to your first post. Quote Link to comment Share on other sites More sharing options...
BIGAL Posted July 3, 2020 Share Posted July 3, 2020 Nice dlanorh maybe atof rather than atoi as example has decimals. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.