Jump to content

Partial Alphabetical Sort


plackowski

Recommended Posts

I'm trying to find the latest revision from a list of revisions. The revisions start with A,B,C... Z, AA, AB, AC...ZZ, AAA, AAB, AAC... Then proceed to numbers 0, 1, 2, 3, etc. Any help would be appreciated!

	(setq testCases (list "A" "AA" "AB" "BD" "AZ" "ZZ" "ZZZ" "11" "10" "9" "2" "1" "0" "Z" "X" "D" "C" "B"))
	
	(setq testCases
		(vl-sort testCases
			  '(lambda ( b a / x y )
				   (setq x (read a) y (read b))
				   (cond
					   (   (and (numberp x) (numberp y)) (< x y))
					   (   (numberp y))
					   (   (numberp x) nil)
					   (   (< a b))
				   )
			   )
		)
	)
	
	(foreach test testCases
		(progn
			(princ "\n")
			(princ test)
		)
	)
    
;Currently produces the following reverse-alphabetical sequence:
;11, 10, 9, 2, 1, 0, ZZZ, ZZ, Z, X, D, C, BD, B, AZ, AB, AA, A
;Desired sequence:
;11, 10, 9, 2, 1, 0, ZZZ, ZZ, BD, AZ, AB, AA, Z, X, D, C, B, A


 

Link to comment
Share on other sites

just cut & pasted this little frankenstein code together , totally expecting master Lee is gonna blow me out of the sky 😉


(defun t1 ( / isnum tl li ln)
  (defun isnum (n)(if (distof n) n nil))
  (setq tl (list "A" "AA" "AB" "BD" "AZ" "ZZ" "ZZZ" "11" "10" "9" "2" "1" "0" "Z" "X" "D" "C" "B"))
  (setq li (vl-remove-if-not 'isnum tl) ln (vl-remove-if 'isnum tl))
  (append (vl-sort li '(lambda (a b)(> (atoi a) (atoi b))))
    (mapcar '(lambda (x)(nth x ln))(vl-sort-i (mapcar '(lambda (y)(apply '+ (vl-string->list y))) ln) '(lambda (a b)(> a b))))
  )
)

 

or in a little different form :

(defun sort (l / s)
  (append (vl-sort (vl-remove-if-not 'distof l) '(lambda (a b)(> (atoi a) (atoi b)))) (mapcar '(lambda (x)(nth x s))
    (vl-sort-i (mapcar '(lambda (y)(apply '+ (vl-string->list y)))(setq s (vl-remove-if 'distof l))) '(lambda (a b)(> a b))))))
    
(sort '("A" "AA" "AB" "BD" "AZ" "ZZ" "ZZZ" "11" "10" "9" "2" "1" "0" "Z" "X" "D" "C" "B"))

 

🐉

Edited by rlx
Link to comment
Share on other sites

The "A" -> "Z" then "AA" , "AB" -> "ZZ" and finally "AAA", "AAB"-> "ZZZ" gives a total of 18278 revisions! Are you ever going to get to the numbers!

 

If you just want to sort alphabetically then

 

(setq testCases (vl-sort testCases '(lambda (x y) (< (apply '+ (vl-string->list x)) (apply '+ (vl-string->list y))))))

 

Link to comment
Share on other sites

10 minutes ago, dlanorh said:

The "A" -> "Z" then "AA" , "AB" -> "ZZ" and finally "AAA", "AAB"-> "ZZZ" gives a total of 18278 revisions! Are you ever going to get to the numbers!

 

 

I think he works for the department that makes the corona rules , I hope the has enough space for all the changes

  • Funny 1
Link to comment
Share on other sites

12 minutes ago, rlx said:

 

I think he works for the department that makes the corona rules , I hope the has enough space for all the changes

 

It's a pity it's not corona brewery 🍻

  • Funny 1
Link to comment
Share on other sites

It seems my complete solution ends up pretty similar to yours @rlx.

 

(defun rh:ansort (lst / nlst alst)
  (foreach x lst (cond ( (vl-every '(lambda (y) (< y 65)) (vl-string->list x)) (setq nlst (cons x nlst)))))
  (setq alst (vl-sort (vl-remove-if '(lambda (x) (vl-position x nlst)) lst) '(lambda (x y) (< (apply '+ (vl-string->list x)) (apply '+ (vl-string->list y)))))
        nlst (vl-sort nlst '(lambda (x y) (< (atoi x) (atoi y))))
        lst (append alst nlst) ;; reverse alst and nlst if you want the numbers first
  );end_setq
);end_defun

 

Link to comment
Share on other sites

1 hour ago, dlanorh said:

The "A" -> "Z" then "AA" , "AB" -> "ZZ" and finally "AAA", "AAB"-> "ZZZ" gives a total of 18278 revisions! Are you ever going to get to the numbers!

 

If you just want to sort alphabetically then

 


(setq testCases (vl-sort testCases '(lambda (x y) (< (apply '+ (vl-string->list x)) (apply '+ (vl-string->list y))))))

 

This returns:

("0" "1" "2" "9" "A" "B" "C" "D" "X" "Z" "10" "11" "AA" "AB" "BD" "AZ" "ZZ" "ZZZ") 

 

Link to comment
Share on other sites

did notice a little flaw in (apply '+ (vl-string->list method so here is a better one :

 

; Gile
(defun a2i (s / i)
  (if (= 0 (setq i (strlen s))) 0 (+ (* (- (ascii (strcase (substr s 1 1))) 64) (expt 26 (1- i)))(a2i (substr s 2)))))

(defun sort (l / s)(append (vl-sort (vl-remove-if-not 'distof l) '(lambda (a b)(> (atoi a)(atoi b))))(mapcar '(lambda (x)
  (nth x s))(vl-sort-i (mapcar '(lambda (y)(a2i y))(setq s (vl-remove-if 'distof l))) '(lambda (a b)(> a b))))))

 

output should now be correct


(sort '("A" "AA" "AB" "BD" "AZ" "ZZ" "ZZZ" "11" "10" "9" "2" "1" "0" "Z" "X" "D" "C" "B"))

 

("11" "10" "9" "2" "1" "0" "ZZZ" "ZZ" "BD" "AZ" "AB" "AA" "Z" "X" "D" "C" "B" "A")

 

🐉

 

 

 

Edited by rlx
  • Thanks 1
Link to comment
Share on other sites

Oops, a badly worded reply. The (apply '+ (vl-string->list..)) method was for alphabetic lists only. The follow up code was for alpha and numeric string lists.

 

 

Link to comment
Share on other sites

Man, you all are fast!

 

No... realistically we'll probably never see more than maybe seven revisions... I just like to be feature complete :P Especially since my string increment function (which I use to get the next revision) is able to reach those values.

 

Follow-up challenge which is probably easier - can the code be simplified if we only need the latest revision? That way the list only needs to be inspected once per drawing?

 

Here's what I've got now, with some test cases:

(defun ABC ( / )
  
	(setq testCases (list "8" "7" "11" "10" "9"))
	(princ "\nSet 1: ")(princ testCases)
	(princ "\nDesired Result: 11")
	(princ "\nActual Result:  ")(princ (_getLatestRev testCases))

	(setq testCases (list "A" "B"))
	(princ "\nSet 2: ")(princ testCases)
	(princ "\nDesired Result: B")
	(princ "\nActual Result:  ")(princ (_getLatestRev testCases))

	(setq testCases (list "21" "0" "FC" "D" "C" "B"))
	(princ "\nSet 3: ")(princ testCases)
	(princ "\nDesired Result: 21")
	(princ "\nActual Result:  ")(princ (_getLatestRev testCases))

	(setq testCases (list "GAK" "GAJ" "GAP" "GAM"))
	(princ "\nSet 4: ")(princ testCases)
	(princ "\nDesired Result: GAP")
	(princ "\nActual Result:  ")(princ (_getLatestRev testCases))

	(setq testCases (list "A" "AA" "AB" "BD" "AZ" "ZZ" "ZZZ" "Z" "X" "D" "C" "B"))
	(princ "\nSet 5: ")(princ testCases)
	(princ "\nDesired Result: ZZZ")
	(princ "\nActual Result:  ")(princ (_getLatestRev testCases))

	(princ)
)

(defun _a2i (s / i)
  (if (= 0 (setq i (strlen s))) 0 (+ (* (- (ascii (strcase (substr s 1 1))) 64) (expt 26 (1- i)))(_a2i (substr s 2)))))

(defun _sort (l / s)(append (vl-sort (vl-remove-if-not 'distof l) '(lambda (a b)(> (atoi a)(atoi b))))(mapcar '(lambda (x)
  (nth x s))(vl-sort-i (mapcar '(lambda (y)(_a2i y))(setq s (vl-remove-if 'distof l))) '(lambda (a b)(> a b))))))
 
(defun _getLatestRev (s / )
  (car (_sort s))
)

 

Link to comment
Share on other sites

Seems to me (car (_sort s)) does the job. At my work our titleblock has its own separate attribute for the main (last) revision and one part of the titleblock has all the project revisions in it. So when I update the revison I read this main revision and then find the matching row in the project part. If all your titleblocks have the same structure this shouldn't be hard to do.

Edited by rlx
Link to comment
Share on other sites

And I think that's the method I'll use, unless there's a method that's simpler computationally. Unfortunately our title block cycles through 5 rows, and once the last row is reached we overwrite the first row. So the latest revision could appear in any of them 🙄

Link to comment
Share on other sites

So I went ahead and complicated my life by sorting dotted pairs instead of revisions. The keys are the rows in the revision block, and the values are the revision number strings. The values are what need sorting, not the keys. I set up a wrapper _sortPairsByValues and a lambda function that uses the sort function from @rlx. And it works! But what I failed to account for are the empty rows in the revision block which return nil values. Unfortunately the sort function sorts nil to the front of the list, not the back. I tried to remove the nils before running the sort, but it doesn't seem to be eliminating them.

 

(defun _sortPairsByValues ( pairList / )
	(setq pairList
		(vl-remove-if 
			'(lambda (a / )
				(= nil (cdr a))
			 )
			pairList
		)
	)	
	(vl-sort pairList
		;comparison function
		'(lambda (a b / c)
			(setq c (_sort (list (cdr a) (cdr b))))
			(= (car c) (cdr a))
		 )
	)
)

 

Link to comment
Share on other sites

what is wrong with

 


(_sortPairsByValues '((0 . "0")(1 . "1")(2 . "2")(3 . nil)))
((2 . "2") (1 . "1") (0 . "0"))

 

looks to me its doing what its suppost to do?

 

Link to comment
Share on other sites

 

(ascii "0")
(ascii "a")
(ascii "A")

("3" "2" "AB" "1" "20" "AA" "AC")
(vl-sort '(3 2 6566 1 20 6565 6567 ) '<)
(1 2 3 20 6565 6566 6567)

Had a think about this from the sorting point of view rather than title question just an idea if you make the items numbers only so 1 2 10 11 etc and A = 65 AA=6565 AZ=6590 it should work so long as less than 65 for numbers  not tested for lots of data.   ABC=656667  Note 0=48 9=57 so could convert numbers also. 20=5057

 

Just looking at the title  revs line by line and keeping is > value much easier.

 

 

 

 

 

Edited by BIGAL
Link to comment
Share on other sites

So the nil filter works fine, I'm just an idiot who left quotes around the nil turning them into strings in all my test cases. Thanks for the help!

 

That's a clever solution BIGAL, I may try to implement that as well.

 

Edit:

I tested the ascii conversion, and I think it would work, but it requires that all the values are padded to the maximum length. Otherwise all the single character letters and single digit numbers will sort below the two character letters.

Edited by plackowski
Link to comment
Share on other sites

Good to hear yes add 00. I did not do any real testing handy for a mixture of alpha and numbers. I did have a bubble sort at one stage not sure if it would work better.

Edited by BIGAL
Link to comment
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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  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...