Updated deepEqual to make it be more accurate
This commit is contained in:
+74
-76
@@ -1,102 +1,100 @@
|
||||
import hscript.Expr.Error;
|
||||
import hscript.Printer;
|
||||
import haxe.Constraints.IMap;
|
||||
import haxe.EnumTools.EnumValueTools;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class Util {
|
||||
// TODO: check this for bugs
|
||||
static function deepEqual(a:Dynamic, b:Dynamic):Bool {
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
static inline function isNull(a:Dynamic):Bool {
|
||||
return Type.typeof(a) == TNull;
|
||||
}
|
||||
|
||||
if (a == null || b == null) {
|
||||
static inline function isFunction(a:Dynamic):Bool {
|
||||
return Type.typeof(a) == TFunction;
|
||||
}
|
||||
|
||||
// TODO: check this for bugs
|
||||
// Code from https://github.com/elnabo/equals/blob/master/src/equals/Equal.hx, (MIT License), but updated to work with haxe 4
|
||||
public static function deepEqual<T> (a:T, b:T) : Bool {
|
||||
if (a == b) { return true; } // if physical equality
|
||||
if (isNull(a) || isNull(b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((a is String) && (b is String)) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
if ((a is Array) && (b is Array)) {
|
||||
var aArray:Array<Dynamic> = cast a;
|
||||
var bArray:Array<Dynamic> = cast b;
|
||||
|
||||
if (aArray.length != bArray.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i in 0...aArray.length) {
|
||||
if (!deepEqual(aArray[i], bArray[i])) {
|
||||
switch (Type.typeof(a)) {
|
||||
case TNull, TInt, TBool, TUnknown:
|
||||
return a == b;
|
||||
case TFloat:
|
||||
return Math.isNaN(cast a) && Math.isNaN(cast b); // only valid true result remaining
|
||||
case TFunction:
|
||||
return Reflect.compareMethods(a, b); // only physical equality can be tested for function
|
||||
case TEnum(_):
|
||||
if (EnumValueTools.getIndex(cast a) != EnumValueTools.getIndex(cast b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var a_args = EnumValueTools.getParameters(cast a);
|
||||
var b_args = EnumValueTools.getParameters(cast b);
|
||||
return deepEqual(a_args, b_args);
|
||||
case TClass(_):
|
||||
if (Std.isOfType(a, String) && Std.isOfType(b, String)) {
|
||||
return a == b;
|
||||
}
|
||||
if (Std.isOfType(a, Array) && Std.isOfType(b, Array)) {
|
||||
var a = cast(a, Array<Dynamic>);
|
||||
var b = cast(b, Array<Dynamic>);
|
||||
if (a.length != b.length) { return false; }
|
||||
for (i in 0...a.length) {
|
||||
if (!deepEqual(a[i], b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (Std.isOfType(a, IMap) && Std.isOfType(b, IMap)) {
|
||||
var a = cast(a, Map<Dynamic, Dynamic>);
|
||||
var b = cast(b, Map<Dynamic, Dynamic>);
|
||||
var a_keys = [ for (key in a.keys()) key ];
|
||||
var b_keys = [ for (key in b.keys()) key ];
|
||||
a_keys.sort(Reflect.compare);
|
||||
b_keys.sort(Reflect.compare);
|
||||
if (!deepEqual(a_keys, b_keys)) { return false; }
|
||||
for (key in a_keys) {
|
||||
if (!deepEqual(a.get(key), b.get(key))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Std.isOfType(a, Date) && Std.isOfType(b, Date)) {
|
||||
return cast(a, Date).getTime() == cast(b, Date).getTime();
|
||||
}
|
||||
|
||||
if (Std.isOfType(a, haxe.io.Bytes) && Std.isOfType(b, haxe.io.Bytes)) {
|
||||
return deepEqual(cast(a, haxe.io.Bytes).getData(), cast(b, haxe.io.Bytes).getData());
|
||||
}
|
||||
|
||||
case TObject:
|
||||
}
|
||||
|
||||
if(Std.isOfType(a, Enum) && Std.isOfType(b, Enum)) {
|
||||
if(Type.enumEq(a, b))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check Map Equality
|
||||
if (Std.isOfType(a, IMap) && Std.isOfType(b, IMap)) {
|
||||
var aMap:IMap<Dynamic, Dynamic> = cast a;
|
||||
var bMap:IMap<Dynamic, Dynamic> = cast b;
|
||||
|
||||
var aFields = [for(v in aMap.keys()) v];
|
||||
var bFields = [for(v in bMap.keys()) v];
|
||||
|
||||
if (aFields.length != bFields.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort fields to ensure consistent comparison
|
||||
aFields.sort(Reflect.compare);
|
||||
bFields.sort(Reflect.compare);
|
||||
|
||||
for (key in aMap.keys()) {
|
||||
if (!bMap.exists(key) || !deepEqual(aMap.get(key), bMap.get(key))) {
|
||||
for (field in Reflect.fields(a)) {
|
||||
var pa = Reflect.field(a, field);
|
||||
var pb = Reflect.field(b, field);
|
||||
if (isFunction(pa)) {
|
||||
// ignore function as only physical equality can be tested, unless null
|
||||
if (isNull(pa) != isNull(pb)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Reflect.isObject(a) && Reflect.isObject(b)) {
|
||||
var aFields = Reflect.fields(a);
|
||||
var bFields = Reflect.fields(b);
|
||||
|
||||
if (aFields.length != bFields.length) {
|
||||
if (!deepEqual(pa, pb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort fields to ensure consistent comparison
|
||||
aFields.sort(Reflect.compare);
|
||||
bFields.sort(Reflect.compare);
|
||||
|
||||
for (i in 0...aFields.length) {
|
||||
var field = aFields[i];
|
||||
if (field != bFields[i] || !deepEqual(Reflect.field(a, field), Reflect.field(b, field))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!Type.enumEq(Type.typeof(a), Type.typeof(b))) {
|
||||
if(Type.typeof(a) == TInt && Type.typeof(b) == TFloat) {
|
||||
return cast(a, Int) == cast(b, Float);
|
||||
}
|
||||
if(Type.typeof(a) == TFloat && Type.typeof(b) == TInt) {
|
||||
return cast(a, Float) == cast(b, Int);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user