XRecord Entmod Duplicate Entry

XRecord Entmod Duplicate Entry

CodeDing
Advisor Advisor
1,262 Views
9 Replies
Message 1 of 10

XRecord Entmod Duplicate Entry

CodeDing
Advisor
Advisor

Hello,

 

I am getting an unexpected result and cannot determine why it's happening. I need some guidance please!

I have an XRecord...

((-1 . <Entity name: 2458ea11b40>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1))

...and with this XRecord, after I create it, I use entmod to add custom DXF code items to it. To do that I use this function...

(defun XRecAdd (xR replaceDups iList / return i)
;xR - XRecord ename
;replaceDups - t or nil
;iList - list containing dotted pair lists <Ex: ((1 . "string") (8 . "Defpoints") ...)>
(setq return nil)
(if (setq xR (entget xR))
  (progn
    (foreach i iList
      (if (assoc (car i) xR)
	(if replaceDups
	  (setq xR (subst (cons (car i) (cdr i)) (assoc (car i) xR) xR))
	  (setq xR (append xR (list (cons (car i) (cdr i)))))
	);if
	(setq xR (append xR (list (cons (car i) (cdr i)))))
      );if
    );foreach
    (entmod xR)
    (setq return t)
  );progn
  (prompt "\nCould not find XRecord to add item(s) to...")
);if
return
);defun

...I won't be posting my entire lisp since it is very lengthy, unless absolutely necessary, so please believe me when I say that I have narrowed it to this particular function.

Now, with my XRecord, after it has been created, I will first add a group of entity handles, as a string, to a DXF 1 code like so...

XRecord In:
xR = <Entity name: 2458ea11b40>
replaceDups = t
iList = ((1 . 6D8,6D9,6FE))
...........
(XRecAdd xR replaceDups iList)
...........
XRecord Out:
((-1 . <Entity name: 2458ea11b40>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1) (1 . 6D8,6D9,6FE))

...this works perfectly fine and returns the expected result.

THEN, when I return to this function to add 2 new items to my XRecord, a DXF 10 code as a point & a DXF 40 code as a double, it returns a DUPLICATE of my DXF 1 code and I have no idea why... So, I inserted a (princ ...) statement to return my XRecord as it was being modified in my function, and it appears that RIGHT BEFORE I get to the (entmod xR) function, I am getting an expected result, then immediately after, I do not. Like so...

XRecord In:
xR = <Entity name: 2458ea11b40> (this includes the additional DXF 1 code already)
replaceDups = t
iList = ((10 380.278 731.166 0.0) (40 . 480.0))
...........
(XRecAdd xR replaceDups iList)
vvv Walk-through vvv
(defun XRecAdd (xR replaceDups iList / return i)
;xR - XRecord ename
;replaceDups - t or nil
;iList - list containing dotted pair lists <Ex: ((1 . "string") (8 . "Defpoints") ...)>
;xR (as-expected) = ((-1 . <Entity name: 2458ea11b40>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1) (1 . 6D8,6D9,6FE))
(setq return nil)
(if (setq xR (entget xR))
  (progn
    (foreach i iList
      (if (assoc (car i) xR)
	(if replaceDups
	  (setq xR (subst (cons (car i) (cdr i)) (assoc (car i) xR) xR))
	  (setq xR (append xR (list (cons (car i) (cdr i)))))
	);if
	(setq xR (append xR (list (cons (car i) (cdr i)))))
      );if
    );foreach
;xR (as-expected) = ((-1 . <Entity name: 2458ea11b40>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1) (1 . 6D8,6D9,6FE) (10 380.278 731.166 0.0) (40 . 480.0))
    (entmod xR)
;xR (Unexpected!) = ((-1 . <Entity name: 2458ea11b40>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1) (1 . 6D8,6D9,6FE) (1 . 6D8,6D9,6FE) (10 380.278 731.166 0.0) (40 . 480.0))
    (setq return t)
  );progn
  (prompt "\nCould not find XRecord to add item(s) to...")
);if
return
);defun
...........
XRecord Out (has Duplicate DXF 1 codes!):
((-1 . <Entity name: 2458ea11b40>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1) (1 . 6D8,6D9,6FE) (1 . 6D8,6D9,6FE) (10 380.278 731.166 0.0) (40 . 480.0))

All help on this issue would be appreciated!

Best,

~DD

0 Likes
Accepted solutions (1)
1,263 Views
9 Replies
Replies (9)
Message 2 of 10

hencoop
Advisor
Advisor

You said "iList = ..." something different in two places.  jfyi

;;;Is there a difference between
(setq xR (subst (cons (car i) (cdr i)) (assoc (car i) xR) xR)); and,
(setq xR (subst i (assoc (car i) xR) xR)); ?
;;; or between
(setq xR (append xR (list (cons (car i) (cdr i))))); and,
(setq xR (append xR (list i))); ?
    (foreach i iList
      (if (assoc (car i) xR)
	(if replaceDups
	  (setq xR (subst (cons (car i) (cdr i)) (assoc (car i) xR) xR)); This or
	  (setq xR (append xR (list (cons (car i) (cdr i))))); this happens if (assoc (car i) xR)
; only the "else" of this "if" can create a duplicate
; when replaceDups is T xR must already have more than one (assoc (car i) xR) for duplicates to result );if (setq xR (append xR (list (cons (car i) (cdr i))))); This happens if (not (assoc (car i) xR))
; this cannot add a duplicate as no (assoc (car i) xR) exists prior to the APPEND );if );foreach

 I would use PRINC to check the values of iList, replaceDups and xR prior to FOREACH to see if there is anything unexpected before xR gets modified.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 3 of 10

CodeDing
Advisor
Advisor

@hencoop ,

 

Thank you for the insightful feedback.

(setq xR (subst i (assoc (car i) xR) xR)); ?
(setq xR (append xR (list i))); ?

...I actually noticed this after I had posted and already updated it. Thank you for pointing that out also.

 

I implemented some feedback to see what "routes" were being used to add to my xR... It appears that (in this particular case) since I have no additional DXF codes in my XRecord yet, it is taking the expected routes and ONLY appending the new values. (it never reaches the "replaceDups" condition since these DXF codes do not exist yet...

    (foreach i iList
      (if (assoc (car i) xR)
	(if replaceDups
	  (progn (setq xR (subst i (assoc (car i) xR) xR))
	    (prompt "\n...TRUE rd - subst...\n") (princ i) (prompt ".....\n"))
	  (progn (setq xR (append xR (list i)))
	    (prompt "\n...FALSE rd - append...\n") (princ i) (prompt ".....\n"))
	);if
	(progn (setq xR (append xR (list i)))
	  (prompt "\n...Append ONLY...\n") (princ i) (prompt ".....\n"))
      );if
    );foreach

Here is my command line returns... (I have my calling function return my XRecord after running "XRecAdd")

((-1 . <Entity name: 1db35f8dd10>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1))...
...Append ONLY...
(1 . 8882,88D0,88F5,8933).....
...((-1 . <Entity name: 1db35f8dd10>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1) (1 . 8882,88D0,88F5,8933))...
...Append ONLY...
(10 2522.9 7393.38 0.0).....
...Append ONLY...
(40 . 720.0).....
...((-1 . <Entity name: 1db35f8dd10>) (0 . XRECORD) ...Boring Stuff... (100 . AcDbXrecord) (280 . 1) (1 . 8882,88D0,88F5,8933) (1 . 8882,88D0,88F5,8933) (10 2522.9 7393.38 0.0) (40 . 720.0))...

 

I am still getting unexpected duplicates.

 

Best,

~DD

 

 

0 Likes
Message 4 of 10

hencoop
Advisor
Advisor

If it never sees replaceDups=T then it must be doing two separate edits to xR.  If iList has duplicate association lists (DXF codes) you will get the duplicate results.  Does your code allow for iList to have duplicates but replaceDups to be NIL?

 

If xR is modified similarly in another piece of your code you can get duplicates.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 5 of 10

CodeDing
Advisor
Advisor

@hencoop ,

 

iList does not provide duplicate entries. Only..

1 instance of DXF 1 code

1 instance of DXF 10 code

1 instance of DXF 40 code

 

Could it perhaps be that I append xR more than once before executing ENTMOD?

 

Attached is a very simplified version of what I am doing which still returns the duplicate entries.

If you are going to run it more than once in the same dwg, just be sure to change the dictionary and xrecord values.

Ex: "TESTdictionary1" --> "TESTdictionary2" ...and... "TESTxrecord1" --> "TESTxrecord2"

 

See attached.

Best,

~DD

0 Likes
Message 6 of 10

CodeDing
Advisor
Advisor

 


Could it perhaps be that I append xR more than once before executing ENTMOD?


...No. This is not the case, I tested it.

 

 

 

0 Likes
Message 7 of 10

hencoop
Advisor
Advisor

Could it perhaps be that I append xR more than once before executing ENTMOD?



It does not appear so within the FOREACH we've been discussing; however, it is possible that elsewhere in your code xR is getting modified.

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 8 of 10

CodeDing
Advisor
Advisor

To anyone reading,

 

I have tested starting with a different DXF code instead of 1 and it still returns a duplicate.

Ex:
(XRecAdd <some ename> t '((1 . "SomeString")))

then trying

(XRecAdd <some ename> t '((2 . "AnotherString")))

ultimately returns

..... (1 . "SomeString") (1 . "SomeString") (2 . "AnotherString") .....

 

and vice-versa:

(XRecAdd <some ename> t '((2 . "SomeString")))

then trying

(XRecAdd <some ename> t '((1 . "AnotherString")))

ultimately returns

..... (2 . "SomeString") (2 . "SomeString") (1 . "AnotherString") .....

 

This is merely to determine that I do not believe the TYPE of DXF code is causing my issue.

0 Likes
Message 9 of 10

hencoop
Advisor
Advisor

I looked a bit closer.

(assoc (car i) xR) will return only the first association list or DXF code dotted pair whose 1st element matches (car i).

If there are more than one the others will never be reached by the code.

You can use (member i xR) to check for an exact match.

 

I think the error is being introduced by (If (assoc (car i) xR) not matching the conditions you intended.

 

You can test it by putting some PRINCs in the sections of code that tell you the content of your variables and/or the status of your test (whether the IF is matched or not, etc.)

 

AutoCAD User since 1989. Civil Engineering Professional since 1983
Product Version: 13.6.1963.0 Civil 3D 2024.4.1 Update Built on: U.202.0.0 AutoCAD 2024.1.6
                        27.0.37.14 Autodesk AutoCAD Map 3D 2024.0.1
                        8.6.52.0 AutoCAD Architecture 2024
0 Likes
Message 10 of 10

CodeDing
Advisor
Advisor
Accepted solution

@hencoop ,

 

According to THIS POST (from 2001!) it appears to be an old and evidently known & not fixed issue!

So, heeding the very elegantly supplied advice:

"Change Xrecord = Delete Xrecord + AddNewXrecord"

I decided to implement this into my Function.

This captures all of the modifications made by the function to the XRecord until RIGHT-BEFORE you would use (entmod ...), then I store the relevant information, delete the old XRecord, and create a new one (with same name/location) in its place. You will notice that I had to also pass the XRecord Owner & XRecord Name into the function. I have chosen to return the new XRecord entity name instead of "t" so that I can store it after being called in my other function, then if I make future calls in that function, the ename will be current.

Here is my updated function:

(defun XRecAdd (xR xrOwner xrName replaceDups iList / return i newXR)
;xR - XRecord ename / xrOwner - XRecord Dictionary ename / xrName - String of XRecord name
;replaceDups - t or nil / iList - list containing dotted pair lists <Ex: ((1 . "string") (8 . "Defpoints") ...)>
(setq return nil)
(if (setq xR (entget xR))
  (progn
    (foreach i iList
      (if (assoc (car i) xR)
	(if replaceDups
	  (setq xR (subst i (assoc (car i) xR) xR))
	  (setq xR (append xR (list i)))
	);if
	(setq xR (append xR (list i)))
      );if
    );foreach
    (setq newXR (cons (cons 0 "XRECORD") (member '(100 . "AcDbXrecord") xR)))
    (dictremove xrOwner xrName)
    (setq return (dictadd xrOwner xrName (entmakex newXR)))
  );progn
  (prompt "\nCould not find XRecord to add item(s) to...")
);if
return
);defun

Thank you for all of your time and help with this!

Best,

~DD

0 Likes