// Use Compare.nui // Abstract class for comparing item based information // From the VDFQuery download by Sture ApS //> pkgdoc.begin //> Class for comparing item based information. This class may be used when you have random access to //> the data that you want to compare (ie. when the data resides in arrays). //> pkgdoc.end Use Base.nui // Item_Property command, Various macros (FOR_EX...), cArray, cSet and cStack classes (No User Interface) // =========================================================================== // cItemBasedCompare class // =========================================================================== define COMPARE_ORDERED for 0 // Formerly: COMPARE_SYNCHRONIZED define COMPARE_UNORDERED for 1 // Formerly: COMPARE_SINGLE_SEQUENTIAL class cItemBasedCompare is an cArray procedure construct_object integer img# forward send construct_object img# //> Setting piStrategy to COMPARE_UNORDERED will perform the comparison //> assuming no particular ordering sequence of the items to be compared. Could be //> used for creating any kind of map for matching for example field names in a //> table with column names in a CSV file. //> //> On the other hand, setting piStrategy to COMPARE_ORDERED will assume that the //> items are to occur in identical order on the two sides. Using this strategy you will //> not be able to detect if entries have been switched. It could be used for comparing //> two text files (if loaded into arrays). property integer piStrategy COMPARE_ORDERED //> This property defines how how far the object will search //> in order to re-syncronize. //> Setting it to 0 (its default value) means there is no limit. property integer piSyncLimit 0 // 0 means no limit property integer pItemStart1 0 //> Lowest index on the primary side. property integer pItemStop1 0 //> Highest index on the primary side. property integer pItemStart2 0 //> Lowest index on the secondary side. property integer pItemStop2 0 //> Highest index on the secondary side. end_procedure //> Return TRUE if the items match. Must be augmented. function iCompareItems.ii integer itm1# integer itm2# returns integer end_function //> This is sent when items are found to be identical. Should be augmented. procedure items_matched integer itm1# integer itm2# end_procedure //> This is sent when an item cannot be matched. //> Means itm# on the left side couldn't be matched. The info parameter //> gives a little extra information. //> -2: The item is missing in the end of the sequence //> -1: Sync limit exceeded. Missing somewhere in the middle of things. //> >=0: Missing from the right side at position info# //> Should be augmented. procedure item_not_matched1 integer itm# integer info# end_procedure //> This is sent when an item cannot be matched //> Means itm# on the right side couldn't be matched. The info parameter //> gives a little extra information. //> -2: The item is missing in the end of the sequence //> -1: Sync limit exceeded. Missing somewhere in the middle of things. //> >=0: Missing from the left side at position info# //> Should be augmented. procedure item_not_matched2 integer itm# integer info# end_procedure // This algorithm will not be able to detect if entries have been switched. It could be used // for comparing two text files. procedure run_synchronized integer current1# current2# integer stop1# stop2# integer tmp_offset# fin# SyncLimit# itm# get pItemStart1 to current1# // Give us where to start and where to get pItemStart2 to current2# // stop on left and right side. get pItemStop1 to stop1# // get pItemStop2 to stop2# // get piSyncLimit to SyncLimit# // Is there a limit repeat ifnot (current1#>stop1# or current2#>stop2#) begin // There are still items to compare if (iCompareItems.ii(self,current1#,current2#)) begin // And the next ones in line do match send items_matched current1# current2# increment current1# increment current2# end else begin // Didn't match move 1 to tmp_offset# move 0 to fin# repeat if ((current2#+tmp_offset#<=stop2#) and iCompareItems.ii(self,current1#,current2#+tmp_offset#)) begin for itm# from current2# to (current2#+tmp_offset#-1) send item_not_matched2 itm# current1# loop send items_matched current1# (current2#+tmp_offset#) increment current1# move (current2#+tmp_offset#+1) to current2# move 1 to fin# end else begin if ((current1#+tmp_offset#<=stop1#) and iCompareItems.ii(self,current1#+tmp_offset#,current2#)) begin for itm# from current1# to (current1#+tmp_offset#-1) send item_not_matched1 itm# current2# loop send items_matched (current1#+tmp_offset#) current2# move (current1#+tmp_offset#+1) to current1# increment current2# move 1 to fin# end end ifnot fin# begin increment tmp_offset# if ((SyncLimit# and (tmp_offset#>SyncLimit#)) or (((current1#+tmp_offset#)>stop1#) and ((current2#+tmp_offset#)>stop2#))) begin // Either sync-limit has been broken, or incrementing the // tmp_offset# variable means that we are about to break the // the stop item limit on one of the sides. // NOTE! It is very important that the two next messages are sent // in this order (first 2 then 1): send item_not_matched2 current2# -1 // means: missing in the middle send item_not_matched1 current1# -1 // means: missing in the middle increment current1# increment current2# move 1 to fin# end end until fin# end end until (current1#>stop1# or current2#>stop2#) for itm# from current1# to stop1# send item_not_matched1 itm# -2 // means: missing in the end loop for itm# from current2# to stop2# send item_not_matched2 itm# -2 // means: missing in the end loop end_procedure procedure private.register_matched integer itm# set value item itm# to 1 end_procedure function private.is_matched integer itm# returns integer function_return (value(self,itm#)) end_function // This one assumes no particular sequence of items in either of the objects // to be compared. Could be used for creating any kind of map for matching // for example field names in a table with column names in a CSV file. procedure run_single_sequential integer current1# current2# integer start1# start2# integer stop1# stop2# integer matched# get pItemStart1 to start1# get pItemStart2 to start2# get pItemStop1 to stop1# get pItemStop2 to stop2# send delete_data for current1# from start1# to stop1# move start2# to current2# move 0 to matched# repeat if (not(private.is_matched(self,current2#)) and iCompareItems.ii(self,current1#,current2#)) begin send private.register_matched current2# send items_matched current1# current2# move 1 to matched# end else increment current2# until (matched# or current2#>stop2#) ifnot matched# send item_not_matched1 current1# loop for current2# from start2# to stop2# ifnot (private.is_matched(self,current2#)) send item_not_matched2 current2# loop end_procedure procedure run integer liStrategy get piStrategy to liStrategy if liStrategy eq COMPARE_ORDERED send run_synchronized if liStrategy eq COMPARE_UNORDERED send run_single_sequential end_procedure end_class // cItemBasedCompare //> Class for comparing sorted data that are not item based. Values are not //> retrieved by item numbers but rather by specifying how to get this next value //> ("find ge" on one side and "readln" on the other for example) //> //> Using this class you indicate the ordering of values (like: this value is //> >=, = or <= than this other value) rather than indicating if they match or //> not (like in the cItemBasedCompare class) //> //> This strategy would be good if comparing records sorted by uniform indices in //> two different tables. //> //> It is also good when you know your in-data are sorted (could be sorted arrays) and you //> need the output from the compare to be sorted as well (like when comparing the list of //> filenames in two directories) //> //> The terminology of the methods and their parameters reflects that the //> values that are compared are not retrieved from an array (are not item //> based) but rather these values are retrieved by running sequentially //> through the records of a table by some index. //> //> The name cDoubleOrderedCompare indicates that both the left side and //> the right side are presumed to be ordered. class cDoubleOrderedCompare is an cArray //> Augment this function in order to seed the left buffer (1). Return TRUE //> if the seeding was succesful. For example: //> //> function iSeed1 returns integer //> clear Customer //> function_Return TRUE //> end_function //> function iSeed1 returns integer end_function //> Augment this function in order to seed the right buffer (2). Return TRUE //> if the seeding was succesful. function iSeed2 returns integer end_function //> The function should be augmented to return the value for the left buffer (1) to be used for comparing. For example: //> //> function sValue1 returns string //> function_return Customer.Name //> end_function //> function sValue1 returns string end_procedure //> The function should be augmented to return the value for the right buffer (2) to be used for comparing. //> //> function sValue2 returns string //> function_return Vendor.Vendor_Name //> end_function //> function sValue2 returns string end_procedure //> Augment to "advance" the left buffer. Return TRUE if advancing was succesful. Could be: //> //> function iAdvance1 returns integer //> find gt Customer by index.2 // Name is most significant in index.2 //> function_return (found) //> end_function //> function iAdvance1 returns integer end_function //> Augment to "advance" the right buffer. Return TRUE if advancing was succesful. //> //> function iAdvance2 returns integer //> find gt Vendor by index.2 //> function_return (found) //> end_function //> function iAdvance2 returns integer end_function //> This is sent when items are found to be identical. //> //> procedure Match string lsVal1 string lsVal2 //> showln "Both in Customer and Vendor tables: " lsVal1 //> end_procedure //> procedure Match string lsVal1 string lsVal2 end_procedure //> This is sent when a left side (1) item cannot be matched. //> //> procedure NotMatched1 string lsVal //> showln "Only found in Customer table: " lsVal //> end_procedure //> procedure NotMatched1 string lsVal end_procedure //> This is sent when a right side (2) item cannot be matched. //> //> procedure NotMatched2 string lsVal //> showln "Only found in Vendor table: " lsVal //> end_procedure //> procedure NotMatched2 string lsVal end_procedure function iCompare.ss string lsVal1 string lsVal2 returns integer if (lsVal1=lsVal2) function_return 0 // Match if (lsVal1 This is sent when items are found to be identical // procedure Match string value1# string value2# // end_procedure // //> This is sent when a left side (1) item cannot be matched // procedure NotMatched1 string value# // end_procedure // //> This is sent when a right side (2) item cannot be matched // procedure NotMatched2 string value# // end_procedure // function iCompare.ss string value1# string value2# returns integer // if value1# eq value2# function_return 1 // //function_return 0 // end_function // procedure run // integer ok1# ok2# comp_res# oStackedValues1# oStackedValues2# // string value1# value2# // move (oStackedValues1(self)) to oStackedValues1# // move (oStackedValues2(self)) to oStackedValues2# // send delete_data to oStackedValues1# // send delete_data to oStackedValues2# // get iSeed1 to ok1# // get iSeed2 to ok2# // while (ok1# or ok2#) // if ok1# get sValue1 to value1# // if ok2# get sValue2 to value2# // if (ok1# and ok2#) begin // // get iCompare.ss value1# value2# to comp_res# // if comp_res# begin // Match // send match value1# value2# // get iAdvance1 to ok1# // get iAdvance2 to ok2# // end // else begin // No match // end // end // else begin // if ok1# begin // send NotMatched1 value1# // get iAdvance1 to ok1# // end // else begin // send NotMatched2 value2# // get iAdvance2 to ok2# // end // end // end // end_procedure // end_class // cDoubleOrderedCompare