If you add the following code to your JavaScript section:
Variable Utility Functions and the Array.indexOfObject() prototype
setup.VarUtils = { /* Variable Utility Functions */
/* isArray: Returns if a value is an array. */
isArray: function (Value) {
return Array.isArray(Value);
},
/* isBoolean: Returns if a value is a boolean. */
isBoolean: function (Value) {
return typeof Value === "boolean";
},
/* isDate: Returns if value is a date object. */
isDate: function (Value) {
return setup.VarUtils.isObject(Value) && Value instanceof Date;
},
/* isFunction: Returns if a value is a function. */
isFunction: function (Value) {
return typeof Value === "function";
},
/* isGenericObject: Returns if a value is a generic object. */
isGenericObject: function (Value) {
return setup.VarUtils.isObject(Value) && Value.constructor === Object;
},
/* isInteger: Returns if a value is an integer. */
isInteger: function (Value) {
return Number.isInteger(Value);
},
/* isMap: Returns if a value is a map. */
isMap: function (Value) {
return setup.VarUtils.isObject(Value) && Value instanceof Map;
},
/* isNull: Returns if a value is null. */
isNull: function (Value) {
return Value === null;
},
/* isNumber: Returns if a value is a number. */
isNumber: function (Value) {
return (typeof Value === "number") && Number.isFinite(Value) && (!Number.isNaN(Value));
},
/* isObject: Returns if a value is an object (not including "null"). */
isObject: function (Value) {
return !!Value && typeof Value === "object";
},
/* isProperty: Returns if Prop is a property of the object Obj. */
isProperty: function (Obj, Prop) {
var result = false;
if (setup.VarUtils.isObject(Obj)) {
result = Obj ? hasOwnProperty.call(Obj, Prop) : false;
if (!result) { /* if not pass... */
try {
if (Obj[Prop] === undefined) {
result = false; /* double-check fail */
} else {
result = true; /* double-check pass */
}
} catch(error) {
result = false; /* error fail */
}
}
}
return result;
},
/* Returns if a value is a regexp. */
isRegExp: function (Value) {
return setup.VarUtils.isObject(Value) && Value.constructor === RegExp;
},
/* isSet: Returns if a value is a set. */
isSet: function (Value) {
return setup.VarUtils.isObject(Value) && Value instanceof Set;
},
/* isString: Returns if a value is a string. */
isString: function (Value) {
return (typeof Value === "string") || (Value instanceof String);
},
/* isUndefined: Returns if a value is undefined. */
isUndefined: function (Value) {
return typeof Value === "undefined";
},
/* spread: Returns a Map, Set, or String converted to an array.
If the second parameter is an Array, Map, Set, or String, then
the two objects are spread and returned as a single array.
If a function is passed as the second parameter, this calls the
function with the spread array as parameters and returns that
function's value. */
spread: function (Value, Funct) {
var arr = [];
if (setup.VarUtils.isArray(Value)) {
arr = clone(Value);
} else if (setup.VarUtils.isMap(Value)) {
Value.forEach(function(val, key, map) {
arr.push([key, val]);
});
} else if (setup.VarUtils.isSet(Value)) {
Value.forEach(function(val, key, set) {
arr.push(val);
});
} else if (setup.VarUtils.isString(Value)) {
arr = Value.split('');
}
if (setup.VarUtils.isFunction(Funct)) {
return Funct.apply(null, arr);
} else if (setup.VarUtils.isObject(Funct)) {
arr = arr.concat(setup.VarUtils.spread(Funct));
}
return arr;
},
/* arraysAreEqual: Check two arrays to see if they're identical.
IgnoreObjectPairs is for internal use to prevent infinite loops
of objects. */
arraysAreEqual: function (Array1, Array2, IgnoreObjectPairs) {
if (setup.VarUtils.isArray(Array1) && setup.VarUtils.isArray(Array2)) {
var i = 0;
if (setup.VarUtils.isUndefined(IgnoreObjectPairs)) {
IgnoreObjectPairs = [];
}
if (IgnoreObjectPairs.length > 0) {
for (i = 0; i < IgnoreObjectPairs.length; i++) {
if (((IgnoreObjectPairs[i][0] === Array1) && (IgnoreObjectPairs[i][1] === Array2)) ||
((IgnoreObjectPairs[i][0] === Array2) && (IgnoreObjectPairs[i][1] === Array1))) {
return true; /* Ignores object pairs that have already been checked to prevent infinite loops. */
}
}
}
IgnoreObjectPairs.push([Array1, Array2]);
if (Array1.length !== Array2.length) {
return false; /* Arrays are not the same length. */
}
if (Array1.length > 0) {
for (i = 0; i < Array1.length; i++) {
if (!setup.VarUtils.valuesAreEqual(Array1[i], Array2[i], IgnoreObjectPairs)) { /* OOO function call. */
return false; /* Values or types do not match. */
}
}
}
return true; /* All values match. */
}
return false; /* Both are not arrays. */
},
/* mapsAreEqual: Returns if two maps contain the same values in the
same order.
IgnoreObjectPairs is for internal use to prevent infinite loops
of objects. */
mapsAreEqual: function (Map1, Map2, IgnoreObjectPairs) {
if (setup.VarUtils.isMap(Map1) && setup.VarUtils.isMap(Map2)) {
if (Map1.size === Map2.size) {
if (setup.VarUtils.isUndefined(IgnoreObjectPairs)) {
IgnoreObjectPairs = [];
}
if (IgnoreObjectPairs.length > 0) {
for (var i = 0; i < IgnoreObjectPairs.length; i++) {
if (((IgnoreObjectPairs[i][0] === Map1) && (IgnoreObjectPairs[i][1] === Map2)) ||
((IgnoreObjectPairs[i][0] === Map2) && (IgnoreObjectPairs[i][1] === Map1))) {
return true; /* Ignores object pairs that have already been checked to prevent infinite loops. */
}
}
}
IgnoreObjectPairs.push([Map1, Map2]);
var a = setup.VarUtils.spread(Map1), b = setup.VarUtils.spread(Map2);
return setup.VarUtils.arraysAreEqual(a, b, IgnoreObjectPairs); /* Compares maps. */
}
}
return false; /* Both are either not maps or are maps of different sizes. */
},
/* objectsAreEqual: Check two objects to see if they're identical.
IgnoreObjectPairs is for internal use to prevent infinite loops
of objects. */
objectsAreEqual: function (Obj1, Obj2, IgnoreObjectPairs) {
if (setup.VarUtils.isObject(Obj1) && setup.VarUtils.isObject(Obj2)) {
var i = 0;
if (setup.VarUtils.isUndefined(IgnoreObjectPairs)) {
IgnoreObjectPairs = [];
}
if (IgnoreObjectPairs.length > 0) {
for (i = 0; i < IgnoreObjectPairs.length; i++) {
if (((IgnoreObjectPairs[i][0] === Obj1) && (IgnoreObjectPairs[i][1] === Obj2)) ||
((IgnoreObjectPairs[i][0] === Obj2) && (IgnoreObjectPairs[i][1] === Obj1))) {
return true; /* Ignores object pairs that have already been checked to prevent infinite loops. */
}
}
}
IgnoreObjectPairs.push([Obj1, Obj2]);
if (setup.VarUtils.isGenericObject(Obj1) && setup.VarUtils.isGenericObject(Obj2)) {
var Keys1 = Object.keys(Obj1).sort(), Keys2 = Object.keys(Obj2).sort();
if (!setup.VarUtils.arraysAreEqual(Keys1, Keys2)) {
return false; /* Objects have a different number of keys or have different keys. */
}
if (Keys1.length > 0) {
var Key;
for (i = 0; i < Keys1.length; i++) {
Key = Keys1[i];
if (!setup.VarUtils.valuesAreEqual(Obj1[Key], Obj2[Key], IgnoreObjectPairs)) { /* OOO function call. */
return false; /* Values do not match. */
}
}
}
return true; /* All values match. */
} else {
return setup.VarUtils.valuesAreEqual(Obj1, Obj2, IgnoreObjectPairs); /* Return whether objects match. OOO function call. */
}
}
return false; /* Both are not objects. */
},
/* setsAreEqual: Returns if two sets contain the same values in the
same order.
IgnoreObjectPairs is for internal use to prevent infinite loops
of objects. */
setsAreEqual: function (Set1, Set2, IgnoreObjectPairs) {
if (setup.VarUtils.isSet(Set1) && setup.VarUtils.isSet(Set2)) {
if (Set1.size === Set2.size) {
if (setup.VarUtils.isUndefined(IgnoreObjectPairs)) {
IgnoreObjectPairs = [];
}
if (IgnoreObjectPairs.length > 0) {
for (var i = 0; i < IgnoreObjectPairs.length; i++) {
if (((IgnoreObjectPairs[i][0] === Set1) && (IgnoreObjectPairs[i][1] === Set2)) ||
((IgnoreObjectPairs[i][0] === Set2) && (IgnoreObjectPairs[i][1] === Set1))) {
return true; /* Ignores object pairs that have already been checked to prevent infinite loops. */
}
}
}
IgnoreObjectPairs.push([Set1, Set2]);
var a = setup.VarUtils.spread(Set1), b = setup.VarUtils.spread(Set2);
return setup.VarUtils.arraysAreEqual(a, b, IgnoreObjectPairs); /* Compares sets. */
}
}
return false; /* Both are either not sets or are sets of different sizes. */
},
/* setsMatch: Returns if two sets contain matches for all values in
any order. */
setsMatch: function (Set1, Set2) {
if (setup.VarUtils.isSet(Set1) && setup.VarUtils.isSet(Set2)) {
if (Set1.size === Set2.size) {
if (Set1.size > 0) { /* Compare sets. */
var setIterator = Set1.values();
var result = setIterator.next();
while (!result.done) {
if (!Set2.has(result.value)) return false; /* Sets do not match. */
result = setIterator.next();
}
}
return true; /* Sets match. */
}
}
},
/* valuesAreEqual: Check two variables to see if they're identical.
This function does not support comparing symbols, functions, or
custom types.
IgnoreObjectPairs is for internal use to prevent infinite loops
of objects. */
valuesAreEqual: function (Var1, Var2, IgnoreObjectPairs) {
if (typeof Var1 === typeof Var2) {
switch (typeof Var1) {
/* String */
case "string":
/* Number */
case "number": // eslint-disable-line no-fallthrough
/* Boolean */
case "boolean": // eslint-disable-line no-fallthrough
return Var1 === Var2; /* Returns whether variables are equal or not. */
/* Undefined */
case "undefined":
return true; /* Variables are both undefined. */
/* Object */
case "object":
/* Array Object */
if (setup.VarUtils.isArray(Var1) && setup.VarUtils.isArray(Var2)) {
return setup.VarUtils.arraysAreEqual(Var1, Var2, IgnoreObjectPairs); /* Return whether arrays are equal. */
/* Generic Object */
} else if (setup.VarUtils.isGenericObject(Var1) && setup.VarUtils.isGenericObject(Var2)) {
return setup.VarUtils.objectsAreEqual(Var1, Var2, IgnoreObjectPairs); /* Return whether generic objects are equal. */
/* Date Object */
} else if (setup.VarUtils.isDate(Var1) && setup.VarUtils.isDate(Var2)) {
return (Var1 - Var2) == 0; /* Returns whether dates are equal. */
/* Map Object */
} else if (setup.VarUtils.isMap(Var1) && setup.VarUtils.isMap(Var2)) {
return setup.VarUtils.mapsAreEqual(Var1, Var2, IgnoreObjectPairs); /* Return whether maps are equal. */
/* Set Object */
} else if (setup.VarUtils.isSet(Var1) && setup.VarUtils.isSet(Var2)) {
return setup.VarUtils.setsAreEqual(Var1, Var2, IgnoreObjectPairs); /* Return whether sets are equal. */
/* Null Object */
} else if ((Var1 === null) && (Var2 === null)) {
return true; /* Objects are both null. */
}
return false; /* Objects either don't match or are of an unsupported type. */
default:
return false; /* Unsupported type. */
}
} else {
return false; /* Variables are not of the same type. */
}
},
/* indexOfObject: Returns the index of the first matching Obj object
in the Arr array or -1 if not found.
Starts at index Idx (or 0 if Idx not included).
If Idx is negative, then it starts at that offset from the end of
the array (to a minimum of 0).
Returns undefined if Arr is not an array. */
indexOfObject: function (Arr, Obj, Idx) {
if (!setup.VarUtils.isArray(Arr)) {
return undefined; // Not an array.
}
if (Arr.length === 0) {
// Empty array.
return -1; // No match.
}
var i;
if (setup.VarUtils.isUndefined(Idx) || !setup.VarUtils.isInteger(Idx)) {
Idx = 0;
}
if (Idx < 0) {
Idx = Arr.length + Idx;
if (Idx < 0) {
Idx = 0;
}
}
for (i = Idx; i < Arr.length; i++) {
if (setup.VarUtils.valuesAreEqual(Obj, Arr[i])) {
return i; // Found a match.
}
}
return -1; // No match.
}
};
Array.prototype.indexOfObject = function(obj, fromIndex) {
return setup.VarUtils.indexOfObject(this, obj, fromIndex);
};
then, instead of .indexOf()
, you can use the .indexOfObject()
method on your arrays to find objects with matching values, even if they have different references. Other than that, the .indexOfObject()
method should work the same as the normal .indexOf()
method.
Additionally, you can use the various setup.VarUtils methods if you need to check the types of your variables.
Enjoy!