plackowski Posted August 25, 2020 Posted August 25, 2020 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 Quote
rlx Posted August 25, 2020 Posted August 25, 2020 (edited) 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 August 25, 2020 by rlx Quote
dlanorh Posted August 25, 2020 Posted August 25, 2020 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)))))) Quote
rlx Posted August 25, 2020 Posted August 25, 2020 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 1 Quote
dlanorh Posted August 25, 2020 Posted August 25, 2020 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 1 Quote
dlanorh Posted August 25, 2020 Posted August 25, 2020 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 Quote
rlx Posted August 25, 2020 Posted August 25, 2020 @dlanorh in the end there are but so many roads leading to Rome Quote
ronjonp Posted August 25, 2020 Posted August 25, 2020 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") Quote
rlx Posted August 26, 2020 Posted August 26, 2020 (edited) 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 August 26, 2020 by rlx 1 Quote
dlanorh Posted August 26, 2020 Posted August 26, 2020 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. Quote
plackowski Posted August 26, 2020 Author Posted August 26, 2020 Man, you all are fast! No... realistically we'll probably never see more than maybe seven revisions... I just like to be feature complete 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)) ) Quote
rlx Posted August 26, 2020 Posted August 26, 2020 (edited) 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 August 26, 2020 by rlx Quote
plackowski Posted August 26, 2020 Author Posted August 26, 2020 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 Quote
plackowski Posted August 26, 2020 Author Posted August 26, 2020 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)) ) ) ) Quote
rlx Posted August 26, 2020 Posted August 26, 2020 (setq lst (vl-remove nil '(1 2 nil 3 4 nil nil))) Quote
plackowski Posted August 26, 2020 Author Posted August 26, 2020 That doesn't work with dotted pairs though :( For example: '((0 . "0")(1 . "1")(2 . "2")(3 . nil)) Quote
rlx Posted August 26, 2020 Posted August 26, 2020 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? Quote
BIGAL Posted August 26, 2020 Posted August 26, 2020 (edited) (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 August 26, 2020 by BIGAL Quote
plackowski Posted August 27, 2020 Author Posted August 27, 2020 (edited) 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 August 27, 2020 by plackowski Quote
BIGAL Posted August 27, 2020 Posted August 27, 2020 (edited) 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 August 27, 2020 by BIGAL Quote
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.