Best way to compare two arrays for "equality" (same subscripts and values)
I've been trying to write a method to compare two local variables, which may be arrays, for "equality" - that is, to see if they have all the same subscripts (if they're arrays) and values. This is the best I've come up with so far - are there any better/simpler approaches out there?
/// Returns true if arrays <var>first</var> and <var>second</var> have all the same subscripts and all
/// the same values at those subscripts. <br />
/// If <var>first</var> and <var>second</var> both happen to be either undefined or unsubscripted variables,
/// returns true if they're both undefined or have the same value.<br />
/// <var>pMessage</var> has details of the first difference found, if any.
Method CompareArrays(ByRef first, ByRef second, Output pMessage) As %Boolean [ ProcedureBlock = 0 ]
{
New tEqual,tRef1,tRef2,tRef1Data,tRef1Value,tRef2Data,tRef2Value
Set pMessage = ""
Set tEqual = 1
Set tRef1 = "first"
Set tRef2 = "second"
While (tRef1 '= "") || (tRef2 '= "") {
#; See if the subscript is the same for both arrays.
#; If not, one of them has a subscript the other doesn't, and they're not equal.
If ($Piece(tRef1,"first",2) '= $Piece(tRef2,"second",2)) {
Set tEqual = 0
Set pMessage = "Different subscripts encountered by $Query: "_
$Case(tRef1,"":"<end>",:tRef1)_"; "_$Case(tRef2,"":"<end>",:tRef2)
Quit
}
Kill tRef1Value,tRef2Value
Set tRef1Data = $Data(@tRef1,tRef1Value)
Set tRef2Data = $Data(@tRef2,tRef2Value)
#; See if the $Data values are the same for the two.
#; This is really only useful to detect if one of the arrays is undefined on the first pass;
#; $Query only returns subscripts with data.
#; This will catch only one being defined, or one being an array and
#; the other being a regular variable.
If (tRef1Data '= tRef2Data) {
Set tEqual = 0
Set pMessage = "$Data("_tRef1_")="_tRef1Data_"; $Data("_tRef2_")="_tRef2Data
Quit
} ElseIf (tRef1Data#2) && (tRef2Data#2) {
#; See if the value at the subscript is the same for both arrays.
#; If not, they're not equal.
If (tRef1Value '= tRef2Value) {
Set tEqual = 0
Set pMessage = tRef1_"="_@tRef1_"; "_tRef2_"="_@tRef2
Quit
}
}
Set tRef1 = $Query(@tRef1)
Set tRef2 = $Query(@tRef2)
}
Quit tEqual
}
I dug up a pre-dynamic objects version of a utility method from a REST test and cleaned it up a bit (hopefully not introducing any bugs in the process):
Comparing them, I only see two things I prefer in my version. First, in this line of your method I would use four-argument $piece with * as the fourth argument, just in case the subscript contains "first" or "second":
Second, I would use a public list with first and second, rather than turning off procedure block for the entire method.
Actually I don't see any value for checking $data for intermediate subscript (and check their consistency only at the most beginning of a function). Here is my [hopefully] simpler version
if $data(@refL) '= $data(@refR) {
// they are not consistent: one is non-array
return 0
}
do {
// fetch next data node subscript and it's value
set refL = $query(@refL, 1, valueL), refR = $query(@refR, 1, valueR)
if refL="" || (refR="") {
quit
}
set subL = $qlength(refL), subR = $qlength(refR)
if subL'=subR || (valueL '= valueR) {
return 0
}
// check each subscipt individually
for i=1:1:subL {
if $qsubscript(refL, i) '= $qsubscript(refR, i) {
return 0
}
}
} while refL'="" && (refR'="")
// only after all checks passed
return refL=refR
DebugArrayCompare()
new
set m(1,1,1)=11,m(1,2)=12,m(2,1)=133
set n(1,1,1)=11,n(1,2)=12,n(2,1)=133
write $$CompareArrays($name(m),$name(n)),!
set n(3,1)=0
write $$CompareArrays($name(m),$name(n)),!
quit
Here's the one I thought up. Does not use indirection.
How is "best" defined here? If you wan't fastest, and shortest, I have two options for you. This following code also works with both locals and globals, and avoids the bug of using $PIECE() to trim off the global/local name which won't work on globals which contain a "(" in their namespace (admittedly unlikely).
This is the fast version:
Not tested for speed, while I expect this version should be rather fast as it compares common parts of both references rather than individual suscripts. Enjoy!
One more hint from Russian forum:
%GCMP - Compares two globals in the same or different namespace.
Social networks
InterSystems resources
Log in or sign up
Log in or create a new account to continue
Log in or sign up
Log in or create a new account to continue
Log in or sign up
Log in or create a new account to continue