Author Topic: Get TAG name List question  (Read 8998 times)

0 Members and 1 Guest are viewing this topic.

jaydee

  • Guest
Get TAG name List question
« on: July 28, 2011, 06:34:31 PM »
Hi.

I got this list. This is a flexible list which length varies and position of the filled value also varies.

(("TAG1" . "aaa") ("TAG2" . "bbb") ("TAG3" . "ccc") ("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . ""))

What i would like to know is the last TAG name that has an attribute value and the first one has NO value.
In this case i want it to return TAG4 = "ddd" and TAG5 ""

This list below only happens lest then 1% of the time. which contain an unfill attribute value before the fill one.
(("TAG1" . "aaa") ("TAG2" . "bbb") ("TAG3" . "") ("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . ""))

Which i still want it to return return TAG4 = "ddd" and TAG5 ""

Thankyou in advance







Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #1 on: July 28, 2011, 07:12:22 PM »
Quick 'n Dirty:

Code: [Select]
(defun foo ( lst )
  (cond
    ( (null (cddr lst)) lst)
    ( (and
        (eq "" (cdadr  lst))
        (eq "" (cdaddr lst))
      )
      (list (car lst) (cadr lst))
    )
    ( (foo (cdr lst)) )
  )
)
Code: [Select]
_$ (foo '(("TAG1" . "aaa") ("TAG2" . "bbb") ("TAG3" . "ccc") ("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . "")))
(("TAG4" . "ddd") ("TAG5" . ""))

_$ (foo '(("TAG1" . "aaa") ("TAG2" . "bbb") ("TAG3" . "") ("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . "")))
(("TAG4" . "ddd") ("TAG5" . ""))

jaydee

  • Guest
Re: Get TAG name List question
« Reply #2 on: July 28, 2011, 07:30:29 PM »
Thankyou Lee, You're the quickest.

Code: [Select]

_$ (foo '(("TAG1" . "aaa") ("TAG2" . "bbb") ("TAG3" . "ccc") ("TAG4" . "ddd") ("TAG5" . "EEE") ("TAG6" . "FFF")))
(("TAG5" . "EEE") ("TAG6" . "FFF"))

_$ (foo '(("TAG1" . "aaa") ("TAG2" . "bbb") [color=red]("TAG3" . "") [/color] ("TAG4" . "ddd") ("TAG5" . "EEE") ("TAG6" . "FFF")))
(("TAG5" . "EEE") ("TAG6" . "FFF"))



I just test this list and if everything was filled or the last TAG was filled in and the result is not what i want,
the result i would like to see in both scenario is
(("TAG6" . "FFF"))

cheers
« Last Edit: July 28, 2011, 07:48:41 PM by jaydee »

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #3 on: July 28, 2011, 07:49:25 PM »
Quick mod:

Code: [Select]
(defun foo ( lst )
  (cond
    ( (null (cddr lst))
      (if (/= "" (cdadr lst)) (cdr lst) lst)
    )
    ( (and
        (eq "" (cdadr  lst))
        (eq "" (cdaddr lst))
      )
      (list (car lst) (cadr lst))
    )
    ( (foo (cdr lst)) )
  )
)

jaydee

  • Guest
Re: Get TAG name List question
« Reply #4 on: July 28, 2011, 08:19:21 PM »
Thanks Lee, Perfect.
I just want to learnt, do you mind explain to me how the FOO function works, just a few words in each line describing what it does?

Cheers

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #5 on: July 28, 2011, 08:34:19 PM »
The function 'foo' is tail-recursive; recursive in that the function calls itself from within its own function definition, and tail-recursive since there are no expressions to be evaluated following the recursive call.

This tail-recursive behaviour is better demonstrated using a trace call:

Code: [Select]
Entering (FOO (("TAG1" . "aaa") ("TAG2" . "bbb") ("TAG3" . "ccc") ("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . "")))
  Entering (FOO (("TAG2" . "bbb") ("TAG3" . "ccc") ("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . "")))
    Entering (FOO (("TAG3" . "ccc") ("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . "")))
      Entering (FOO (("TAG4" . "ddd") ("TAG5" . "") ("TAG6" . "")))
      Result:  (("TAG4" . "ddd") ("TAG5" . ""))
    Result:  (("TAG4" . "ddd") ("TAG5" . ""))
  Result:  (("TAG4" . "ddd") ("TAG5" . ""))
Result:  (("TAG4" . "ddd") ("TAG5" . ""))

Notice that when the last recursive call is completed, the result follows all the way back up the stack and is returned. This thread is an interesting read on tail-recursion.

Here is a brief outline of the inner workings of the function:

Code: [Select]
(defun foo ( lst )
  (cond
    ( (null (cddr lst)) ;; two items in the list
      (if (/= "" (cdadr lst)) ;; second item is paired with non-empty string
        (cdr lst) ;; return last item
        lst ;; else return both items
      )     
    )
    ( (and
        (eq "" (cdadr  lst)) ;; second item is paired with empty string
        (eq "" (cdaddr lst)) ;; third item is paired with empty string
      )
      (list (car lst) (cadr lst)) ;; return first and second items
    )
    ( (foo (cdr lst)) ) ;; Else recursively process rest of list
  )
)

Note that an iterative function could also be written:

Code: [Select]
(defun foo_iter ( lst )
  (while
    (and (cddr lst)
      (not
        (and
          (eq "" (cdadr  lst))
          (eq "" (cdaddr lst))
        )
      )
    )
    (setq lst (cdr lst))
  )
  (if (/= "" (cdadr (setq lst (list (car lst) (cadr lst)))))
    (cdr lst)
    lst
  )
)

jaydee

  • Guest
Re: Get TAG name List question
« Reply #6 on: July 28, 2011, 08:57:25 PM »
Hi.
I would assume it would stop after it found the first EMPTY string, so where does the code say not to stop looking after the first empty string, and since it doen't stop at first empty string, then why does it stop when for agument sake if there are 2 empty string prior.
ie
(foo '(("TAG1" . "aaa") ("TAG2" . "bbb") ("TAG3" . "")  ("TAG33" . "")("TAG4" . "ddd") ("TAG5" . "EEE")("TAG6" . "")))
(("TAG2" . "bbb") ("TAG3" . ""))

cheers

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Get TAG name List question
« Reply #7 on: July 29, 2011, 03:55:07 AM »
What about this one?
Code: [Select]
(defun TagTest (lst / i)
  (list (setq i (car (reverse (vl-remove-if '(lambda (item) (eq (cdr item) "")) lst))))
        (cadr (member i lst))
  )
)
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Get TAG name List question
« Reply #8 on: July 29, 2011, 04:10:13 AM »
Actually just though about what happens if there's 2 duplicate entries. In which case this should work better:
Code: [Select]
(defun TagTest (lst / i)
  (list (setq i (car (vl-remove-if '(lambda (item) (eq (cdr item) "")) (setq lst (reverse lst)))))
        (nth (1- (vl-position i lst)) lst)
  )
)
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #9 on: July 29, 2011, 07:27:19 AM »
I would assume it would stop after it found the first EMPTY string, so where does the code say not to stop looking after the first empty string, and since it doen't stop at first empty string, then why does it stop when for agument sake if there are 2 empty string prior.

No, it will only stop if either there are only two items remaining in the list, or there are two consecutive empty strings.


Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #10 on: July 29, 2011, 07:50:59 AM »
Actually just though about what happens if there's 2 duplicate entries. In which case this should work better

Nice ideas Irne, this follows the same theme:

Code: [Select]
(defun foo ( lst / i )
  (setq i (length lst))
  (vl-some '(lambda ( x ) (setq i (1- i)) (/= "" (cdr x))) (reverse lst))
  (cond
    ( (= i (1- (length lst)))
      (list (last lst))
    )
    ( (list (nth i lst) (nth (1+ i) lst)) )
  )
)

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Get TAG name List question
« Reply #11 on: July 29, 2011, 08:01:12 AM »
Thanks! Or even more "simply" and not needing to reverse - just iterate from the back:
Code: [Select]
(defun TagTest1 (lst / n)
  (setq n (length lst))
  (while (and (> (setq n (1- n)) 0) (eq (cdr (nth n lst)) "")))
  (list (nth n lst) (nth (1+ n) lst))
)

Edit: Though looking at your latest code, I think yours would be just a tad faster!  :kewl:
« Last Edit: July 29, 2011, 08:10:57 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #12 on: July 29, 2011, 08:29:56 AM »
Edit: Though looking at your latest code, I think yours would be just a tad faster!  :kewl:

I doubt there would be very much in it  :wink:

Don't forget this condition though:

I just test this list and if everything was filled or the last TAG was filled in and the result is not what i want,
the result i would like to see in both scenario is
(("TAG6" . "FFF"))
:-)

jaydee

  • Guest
Re: Get TAG name List question
« Reply #13 on: July 29, 2011, 01:38:45 PM »
Thankyou Lee and Irneb.

These functions works perfect for me, as i mentioned that a empty string before the filled string only happens less then 1% of the time.

verymuch appriciated for your help.

jaydee

  • Guest
Re: Get TAG name List question
« Reply #14 on: July 31, 2011, 06:43:40 AM »
Hi.
I came across one little issue with the blank values
Not very often i get this, but it does happens that the blank values NOT REALLY BLANK (ie  spaces "", " ", "   "),
what make this so difficult is that its not consistent. ie blank with no space, blank with 1 or 2 or more spaces.
I tried to mod the (foo) function to include the "OR" operator, but it might not be able to cover all combinations.
So how would i make it foolproof regardless of blank values with spaces or not?

Code: [Select]

(defun foo ( lst )
  (cond
    ( (null (cddr lst))
      (if (or (/= "" (cdadr lst))
              (/= " " (cdadr lst))) (cdr lst) lst)
    )
    ( (or
       (and (eq "" (cdadr  lst)) (eq "" (cdaddr lst)))
       (and (eq " " (cdadr  lst)) (eq " " (cdaddr lst)))
      )

      (list (car lst) (cadr lst))
    )
    ( (foo (cdr lst)))
  )
)


Thankyou

Stefan

  • Bull Frog
  • Posts: 319
  • The most I miss IRL is the Undo button
Re: Get TAG name List question
« Reply #15 on: July 31, 2011, 07:37:21 AM »
This is mine...
Code: [Select]
(defun foo (lst / a val)
  (setq lst (reverse lst))
  (while
    (and lst
(or
   (not (setq val (vl-string->list (cdar lst))))
   (vl-every '(lambda (x) (= x 32)) val)
   )
)
    (setq a (car lst)
  lst (cdr lst)
  )
    )
  (if a (list (car lst) a) (list (car lst)))
  )
But I wonder, what result would you want for this list '(("TAG1" . " ") ("TAG2" . "") ("TAG3" . "") ("TAG4" . "   "))

My function returns (nil ("TAG1" . " "))

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #16 on: July 31, 2011, 07:57:22 AM »
Try:

Code: [Select]
(defun foo ( lst )
  (cond
    ( (null (cddr lst))
      (if
        (or
          (wcmatch (cdadr lst) "*@*")
          (wcmatch (cdadr lst) "*#*")
        )
        (cdr lst)
        lst
      )
    )
    ( (and
        (wcmatch (cdadr  lst) "~*@*")
        (wcmatch (cdadr  lst) "~*#*")
        (wcmatch (cdaddr lst) "~*@*")
        (wcmatch (cdaddr lst) "~*#*")
      )
      (list (car lst) (cadr lst))
    )
    ( (foo (cdr lst)))
  )
)

But I should imagine it could be written better.

jaydee

  • Guest
Re: Get TAG name List question
« Reply #17 on: July 31, 2011, 10:24:22 AM »
Hi.
This code from LM provide the correct result as long as theres no SPACES as NULL value

Code: [Select]
(defun foo ( lst )
  (cond
    ( (null (cddr lst))
      (if (/= "" (cdadr lst)) (cdr lst) lst)
    )
    ( (and
        (eq "" (cdadr  lst))
        (eq "" (cdaddr lst))
      )
      (list (car lst) (cadr lst))
    )
    ( (foo (cdr lst)) )
  )
)


(foo '(("TAG1" . "") ("TAG2" . "") ("TAG3" . "") ("TAG4" . "") ("TAG5" . "")("TAG6" . "")))
===> (("TAG1" . "") ("TAG2" . ""))

(foo '(("TAG1" . "AAA") ("TAG2" . "BBB") ("TAG3" . "CCC") ("TAG4" . "") ("TAG5" . "")("TAG6" . "")))
===> (("TAG3" . "CCC") ("TAG4" . ""))

(foo '(("TAG1" . "AAA") ("TAG2" . "BBB") ("TAG3" . "") ("TAG4" . "DDD") ("TAG5" . "")("TAG6" . "")))
===> (("TAG4" . "DDD") ("TAG5" . ""))

(foo '(("TAG1" . "AAA") ("TAG2" . "") ("TAG3" . "CCC") ("TAG4" . "DDD") ("TAG5" . "EEE")("TAG6" . "")))
===> (("TAG5" . "EEE") ("TAG6" . ""))

(foo '(("TAG1" . "AAA") ("TAG2" . "BBB") ("TAG3" . "CCC") ("TAG4" . "DDD") ("TAG5" . "EEE")("TAG6" . "FFF")))
===> (("TAG6" . "FFF"))



The latest (foo) function from Lee still treat a NULL value with space as real value

Code: [Select]
(foo '(("TAG1" . "AAA") ("TAG2" . "BBB") ("TAG3" . " ") ("TAG4" . "DDD") ("TAG5" . " ")("TAG6" . " ")))
===> (("TAG6" . " "))

Corrcect result should be

===> (("TAG4" . "DDD") ("TAG5" . ""))



The foo function above from Lee is what i want, the issue is it treats a NULL value with space in it as a real value.
My experience is that

( _  indicates null value with space)

a "" value is common
a "_" null value with single space is very rare
a "__" I have not come across null value with 2 or more spaces.

Hope i explain properly.

Thankyou


Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #18 on: July 31, 2011, 05:39:27 PM »
Using my code in reply #16:

Code: [Select]
_$ (foo '(("TAG1" . "AAA") ("TAG2" . "BBB") ("TAG3" . " ") ("TAG4" . "DDD") ("TAG5" . " ")("TAG6" . " ")))
(("TAG4" . "DDD") ("TAG5" . " "))

 :?

jaydee

  • Guest
Re: Get TAG name List question
« Reply #19 on: July 31, 2011, 07:53:50 PM »
Sorry about this lee. Its early in the morning and i must have all these different (foo) functions loaded for testing.
You are absolutely right.

Thankyou

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Get TAG name List question
« Reply #20 on: August 01, 2011, 07:17:40 AM »
Perhaps this as well?
Code: [Select]
(defun TagTest (lst / n)
  (setq n (length lst))
  (while (and (>= (setq n (1- n)) 0) (eq (vl-string-trim " " (cdr (nth n lst))) "")))
  (if (>= n 0)
    (list (nth n lst) (nth (1+ n) lst))
    (list (last lst))
  )
)

Edit: Just re-read again. That odd-condition (i.e. (("TAG6" . "FFF"))) is making my head spin  :| . In this case Lee's recursive method's best for your case.
« Last Edit: August 01, 2011, 07:21:36 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #21 on: August 01, 2011, 08:27:11 AM »
Good call with the vl-string-trim method - much easier than all my wildcard patterns.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Get TAG name List question
« Reply #22 on: August 01, 2011, 08:59:52 AM »
Thanks. I'm just trying to figure out what to do with that all used / all not used condition. AFAICT it seems that if the last tag "Tag6" already has a (non-blank) value you want it, and only it in the returned list?  :|

What would you want if there are only blank values in the list? Only (("Tag1" . "")) ?

If that's the case then maybe this would "finally"  be working:
Code: [Select]
(defun TagTest (lst / n)
  (setq n (length lst))
  (while (and (>= (setq n (1- n)) 0) (eq (vl-string-trim " " (cdr (nth n lst))) "")))
  (if (>= n 0)
    (cons (nth n lst) (nth (1+ n) lst))
    (cons (car lst) nil)
  )
)
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Get TAG name List question
« Reply #23 on: August 01, 2011, 09:14:45 AM »
Aggggg! I'm being "stupid"!
Code: [Select]
(defun TagTest (lst / n)
  (setq n (length lst))
  (while (and (>= (setq n (1- n)) 0) (eq (vl-string-trim " " (cdr (nth n lst))) "")))
  (if (>= n 0)
    (if (< (1+ n) (length lst))
      (list (nth n lst) (nth (1+ n) lst))
      (list (nth n lst))
    )
    (list (car lst))
  )
)
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

jaydee

  • Guest
Re: Get TAG name List question
« Reply #24 on: August 01, 2011, 06:54:05 PM »
Thankyou Irned.
Your method seem to work very well.

I use Lee's method to test the the return list length, if 1 then last one is fill.

With your method, all filled or all empty will return one single dotted pair. therefore i need to test the value, if NULL value then its the first in the list, else last in the list.

Thankyou for your help


One more question?

Where can i find all the reference on these
I knew about these
car
cdr
caddr, cadddr

I have not come across these.
cdadr
cdaddr

Just found this
http://www.lispworks.com/documentation/HyperSpec/Body/f_car_c.htm

CAR, CDR, CAAR, CADR, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR, CDAAR, CDADR, CDDAR, CDDDR, CAAAAR, CAAADR, CAADAR, CAADDR, CADAAR, CADADR, CADDAR, CADDDR, CDAAAR, CDAADR, CDADAR, CDADDR, CDDAAR, CDDADR, CDDDAR, CDDDDR

I haven't seen anything like this in autodesk reference guide.
« Last Edit: August 01, 2011, 08:25:52 PM by jaydee »

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Get TAG name List question
« Reply #25 on: August 02, 2011, 04:05:43 AM »
You're correct. I can't remember where I read it first. But basically those C****R functions work in the following way:
  • cadr => (car (cdr list))
  • caddr => (car (cdr (cdr list)))
  • cdddr => (cdr (cdr (cdr list)))
  • cdaddr => (cdr (car (cdr (cdr list))))
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12916
  • London, England
Re: Get TAG name List question
« Reply #26 on: August 02, 2011, 07:58:15 AM »
That link provides a great explanation  :-)

Here is my short & simple take on it:

Dotted pairs  -  (x . y)

car:  returns first item
cdr:  returns the last item

Code: [Select]
_$ (car '(1 . 2))
1
_$ (cdr '(1 . 2))
2

All other c*r functions will error with dotted pairs since the above functions return an atom, not a list.

Standard Lists  -  (a b c ... x y z)

[ Note that a,b,c etc could also be lists themselves ]

There are only two functions you need to know:

car:  returns the first item of a list
cdr:  returns the list with the first item removed (note how this is different from dotted pairs)

Code: [Select]
_$ (car '(1 2))
1
_$ (cdr '(1 2))
(2)
_$ (car '(1 2 3 4 5))
1
_$ (cdr '(1 2 3 4 5))
(2 3 4 5)
_$ (car '((1 2) 3 4 5))
(1 2)
_$ (cdr '((1 2) 3 4 5))
(3 4 5)

These two functions can be combined up to four times:

Code: [Select]
(caar x)   =  (car (car x))
(cadr x)   =  (car (cdr x))
(cdar x)   =  (cdr (car x))
(cddr x)   =  (cdr (cdr x))

(caaar x)  =  (car (car (car x)))
(caadr x)  =  (car (car (cdr x)))
(cadar x)  =  (car (cdr (car x)))
(cdaar x)  =  (cdr (car (car x)))
(caddr x)  =  (car (cdr (cdr x)))
(cddar x)  =  (cdr (cdr (car x)))
(cdadr x)  =  (cdr (car (cdr x)))
(cdddr x)  =  (cdr (cdr (cdr x)))

(caaaar x) =  (car (car (car (car x)))
(caaadr x) =  (car (car (car (cdr x)))
(caadar x) =  (car (car (cdr (car x)))
(cadaar x) =  (car (cdr (car (car x)))
(caaddr x) =  (car (car (cdr (cdr x)))
(caddar x) =  (car (cdr (cdr (car x)))
(cadadr x) =  (car (cdr (car (cdr x)))
(cadddr x) =  (car (cdr (cdr (cdr x)))
(cdaaar x) =  (cdr (car (car (car x)))
(cdaadr x) =  (cdr (car (car (cdr x)))
(cdadar x) =  (cdr (car (cdr (car x)))
(cddaar x) =  (cdr (cdr (car (car x)))
(cdaddr x) =  (cdr (car (cdr (cdr x)))
(cdddar x) =  (cdr (cdr (cdr (car x)))
(cddadr x) =  (cdr (cdr (car (cdr x)))
(cddddr x) =  (cdr (cdr (cdr (cdr x)))