;+ ; NAME: ; COMPARE_STRUCT ; PURPOSE: ; Compare all matching tag names and return differences ; ; EXPLANATION: ; Compare all matching Tags names (except for "except_Tags") ; between two structure arrays (may have different struct.definitions), ; and return a structured List of fields found different. ; ; CALLING SEQUENCE: ; diff_List = compare_struct( struct_A, struct_B [ EXCEPT=, /BRIEF, ; /FULL, /NaN, /RECUR_A, /RECUR_B ) ; INPUTS: ; struct_A, struct_B : the two structure arrays to compare. ; Struct_Name : for internal recursion use only. ; OPTIONAL INPUT KEYWORDS: ; EXCEPT = string array of Tag names to ignore (NOT to compare). ; /BRIEF = number of differences found for each matching field ; of two structures is printed. ; /FULL = option to print even if zero differences found. ; /NaN = if set, then tag values are considered equal if they ; are both set to NaN ; /RECUR_A = option to search for Tag names ; in sub-structures of struct_A, ; and then call compare_struct recursively ; for those nested sub-structures. ; /RECUR_B = search for sub-structures of struct_B, ; and then call compare_struct recursively ; for those nested sub-structures. ; Note: ; compare_struct is automatically called recursively ; for those nested sub-structures in both struct_A and struct_B ; (otherwise cannot take difference) ; OUTPUT: ; Returns a structure array describing differences found. ; which can be examined using print,diff_List or help,/st,diff_List. ; The tags are ; TAG_NUM_A - the tag number in structure A ; TAG_NUM_B - the tag number in structure B ; FIELD - the tag name ; NDIFF - number of differences (always 1 for a scalar tag). ; PROCEDURE: ; Match Tag names and then use where function on tags. ; EXAMPLE: ; Find the tags in the !X system variable which are changed after a ; simple plot. ; IDL> x = !X ;Save original values ; IDL> plot, indgen(25) ;Make a simple plot ; IDL> print,compare_struct(x,!X) ;See how structure has changed ; ; and one will see that the tags !X.crange, !X.S, !X.WINDOW and !X.CRANGE are ; changed ; by the plot. ; MODIFICATION HISTORY: ; written 1990 Frank Varosi STX @ NASA/GSFC (using copy_struct) ; modif Aug.90 by F.V. to check and compare same # of elements only. ; Added /NaN keyword W. Landsman March 2004 ; Don't test string for NaN values W. Landsman March 2008 ; Don't test object for NaN values W. Landsman Sep 2017 ; Now returns an empty list when comparing equal hashes or lists ; W. Landsman October 2017 ; NaN keyword passed in recursive calls W. Landsman/R. Colomina Jan 2018 ;- function compare_struct, struct_A, struct_B, EXCEPT=except_Tags, Struct_Name, $ FULL=full, BRIEF=brief, NaN = NaN, $ RECUR_A = recur_A, RECUR_B = recur_B compile_opt idl2 common compare_struct, defined if N_params() LT 2 then begin print,'Syntax - diff_List = compare_struct(struct_A, struct_B ' print,' [EXCEPT=, /BRIEF, /FULL, /NaN, /RECUR_A, /RECUR_B ]' if N_elements(diff_List) GT 0 then return, diff_List else return, -1 endif if N_elements( defined ) NE 1 then begin diff_List = { DIFF_LIST, Tag_Num_A:0, Tag_Num_B:0, $ Field:"", Ndiff:0L } defined = N_tags( diff_List ) endif else diff_List = replicate( {DIFF_LIST}, 1 ) Ntag_A = N_tags( struct_A ) if (Ntag_A LE 0) then begin message," 1st argument must be a structure variable",/CONTIN return,diff_List endif Ntag_B = N_tags( struct_B ) if (Ntag_B LE 0) then begin message," 2nd argument must be a structure variable",/CONTIN return,diff_List endif N_A = N_elements( struct_A ) N_B = N_elements( struct_B ) if (N_A LT N_B) then begin message,"comparing "+strtrim(N_A,2)+" of first structure",/CON message,"to first "+strtrim(N_A,2)+" of "+strtrim(N_B,2)+ $ " in second structure",/CONTIN diff_List = compare_struct( struct_A, struct_B[0:N_A-1], $ EXCEPT=except_Tags, $ RECUR_A = recur_A, $ RECUR_B = recur_B, $ FULL=full, BRIEF=brief, NaN = NaN ) return,diff_List endif else if (N_A GT N_B) then begin message,"comparing first "+strtrim(N_B,2)+" of "+ $ strtrim(N_A,2)+" in first structure",/CON message,"to "+strtrim(N_B,2)+" in second structure",/CONTIN diff_List = compare_struct( struct_A[0:N_B-1], struct_B, $ EXCEPT=except_Tags, $ RECUR_A = recur_A, $ RECUR_B = recur_B, $ FULL=full, BRIEF=brief, NaN = NaN ) return,diff_List endif Tags_A = tag_names( struct_A ) Tags_B = tag_names( struct_B ) wB = indgen( N_elements( Tags_B ) ) Nextag = N_elements( except_Tags ) if (Nextag GT 0) then begin except_Tags = [strupcase( except_Tags )] for t=0,Nextag-1 do begin w = where( Tags_B NE except_Tags[t], Ntag_B ) Tags_B = Tags_B[w] wB = wB[w] endfor endif if N_elements( struct_name ) NE 1 then sname = "." $ else sname = struct_name + "." for t = 0, Ntag_B-1 do begin wA = where( Tags_A EQ Tags_B[t] , nf ) if (nf GT 0) then begin tA = wA[0] tB = wB[t] NtA = N_tags( struct_A.(tA) ) NtB = N_tags( struct_B.(tB) ) if (NtA GT 0 ) && (NtB GT 0) then begin if keyword_set( full ) || keyword_set( brief ) then $ print, sname + Tags_A[tA], " :" diffs = compare_struct( struct_A.(tA), struct_B.(tB), $ sname + Tags_A[tA], $ EXCEPT=except_Tags, $ FULL=full, BRIEF=brief, NaN = NaN ) diff_List = [ diff_List, diffs ] endif else if (NtA LE 0) && (NtB LE 0) then begin x1 = struct_b.(tB) x2 = struct_a.(tA) szx1 = size(x1,/tname) szx2 = size(x2,/tname) dofinite = keyword_set(NaN) && $ (szx1 NE 'STRING') && (szx1 NE 'OBJREF') && $ (szx2 NE 'STRING') && (szx2 NE 'OBJREF') if dofinite then begin g = where( finite(x1) OR finite(x2), Ndiff ) if Ndiff GT 0 then $ w = where( x1[g] NE x2[g], Ndiff ) endif else begin xx = struct_B.(tB) NE struct_A.(tA) ;Handle hashes and lists different from other variable types if N_elements(xx) EQ 0 then Ndiff = 0 else $ w = where( struct_B.(tB) NE struct_A.(tA) , Ndiff ) endelse if (Ndiff GT 0) then begin diff = replicate( {DIFF_LIST}, 1 ) diff.Tag_Num_A = tA diff.Tag_Num_B = tB diff.Field = sname + Tags_A[tA] diff.Ndiff = Ndiff diff_List = [ diff_List, diff ] endif if keyword_set( full ) || $ (keyword_set( brief ) && (Ndiff GT 0)) then $ print, Tags_A[tA], Ndiff, FORM="(15X,A15,I9)" endif else print, Tags_A[ta], " not compared" endif endfor if keyword_set( recur_A ) then begin for tA = 0, Ntag_A-1 do begin if N_tags( struct_A.(tA) ) GT 0 then begin diffs = compare_struct( struct_A.(tA), struct_B, $ sname + Tags_A[tA], $ EXCEPT=except_Tags, $ RECUR_A = recur_A, $ RECUR_B = recur_B, $ FULL=full, BRIEF=brief, NaN = NaN ) diff_List = [ diff_List, diffs ] endif endfor endif if keyword_set( recur_B ) then begin for tB = 0, Ntag_B-1 do begin if N_tags( struct_B.(tB) ) GT 0 then begin diffs = compare_struct( struct_A, struct_B.(tB), $ sname + Tags_B[tB], $ EXCEPT=except_Tags, $ RECUR_A = recur_A, $ RECUR_B = recur_B, $ FULL=full, BRIEF=brief, NaN = NaN ) diff_List = [ diff_List, diffs ] endif endfor endif w = where( [diff_List.Ndiff] GT 0, np ) if (np LE 0) then w = [0] return, diff_List[w] end