Compare arrays as (multi-) sets - javascript

I'm looking for an efficient way to find out whether two arrays contain same amounts of equal elements (in the == sense), in any order:
foo = {/*some object*/}
bar = {/*some other object*/}
a = [1,2,foo,2,bar,2]
b = [bar,2,2,2,foo,1]
sameElements(a, b) --> true
PS. Note that pretty much every solution in the thread uses === and not == for comparison. This is fine for my needs though.

Update 5
I posted a new answer with a different approach.
Update
I extended the code to have the possibility of either checking by reference or equality
just pass true as second parameter to do a reference check.
Also I added the example to Brunos JSPerf
It runs at about 11 ops/s doing a reference check
I will comment the code as soon(!) as I get some spare time to explain it a bit more, but at the moment don't have the time for that, sry. Done
Update 2.
Like Bruno pointed out in the comments sameElements([NaN],[NaN]) yields false
In my opinion this is the correct behaviour as NaN is ambigious and should always lead to a false result,at least when comparing NaN.equals(NaN). But he had quite a good point.
Whether
[1,2,foo,bar,NaN,3] should be equal to [1,3,foo,NaN,bar,2] or not.
Ok.. honestly I'm a bit torn whether it should or not, so i added two flags.
Number.prototype.equal.NaN
If true
NaN.equals(NaN) //true
Array.prototype.equal.NaN
If true
[NaN].equals([NaN],true) //true
note this is only for reference checks. As a deep check would invoke Number.prototype.equals anyway
Update 3:
Dang i totally missed 2 lines in the sort function.
Added
r[0] = a._srt; //DANG i totally missed this line
r[1] = b._srt; //And this.
Line 105 in the Fiddle
Which is kind of important as it determines the consistent order of the Elements.
Update 4
I tried to optimize the sort function a bit, and managed to get it up to about 20 ops/s.
Below is the updated code, as well as the updated fiddle =)
Also i chose to mark the objects outside the sort function, it doesn't seem to make a performance difference anymore, and its more readable
Here is an approach using Object.defineProperty to add equals functions to
Array,Object,Number,String,Boolean's prototype to avoid typechecking in one function for
performance reasons. As we can recursively call .equals on any element.
But of course checking Objects for equality may cause performance issues in big Objects.
So if anyone feels unpleasant manipulating native prototypes, just do a type check and put it into one function
Object.defineProperty(Boolean.prototype, "equals", {
enumerable: false,
configurable: true,
value: function (c) {
return this == c; //For booleans simply return the equality
}
});
Object.defineProperty(Number.prototype, "equals", {
enumerable: false,
configurable: true,
value: function (c) {
if (Number.prototype.equals.NaN == true && isNaN(this) && c != c) return true; //let NaN equals NaN if flag set
return this == c; // else do a normal compare
}
});
Number.prototype.equals.NaN = false; //Set to true to return true for NaN == NaN
Object.defineProperty(String.prototype, "equals", {
enumerable: false,
configurable: true,
value: Boolean.prototype.equals //the same (now we covered the primitives)
});
Object.defineProperty(Object.prototype, "equals", {
enumerable: false,
configurable: true,
value: function (c, reference) {
if (true === reference) //If its a check by reference
return this === c; //return the result of comparing the reference
if (typeof this != typeof c) {
return false; //if the types don't match (Object equals primitive) immediately return
}
var d = [Object.keys(this), Object.keys(c)],//create an array with the keys of the objects, which get compared
f = d[0].length; //store length of keys of the first obj (we need it later)
if (f !== d[1].length) {//If the Objects differ in the length of their keys
return false; //immediately return
}
for (var e = 0; e < f; e++) { //iterate over the keys of the first object
if (d[0][e] != d[1][e] || !this[d[0][e]].equals(c[d[1][e]])) {
return false; //if either the key name does not match or the value does not match, return false. a call of .equal on 2 primitives simply compares them as e.g Number.prototype.equal gets called
}
}
return true; //everything is equal, return true
}
});
Object.defineProperty(Array.prototype, "equals", {
enumerable: false,
configurable: true,
value: function (c,reference) {
var d = this.length;
if (d != c.length) {
return false;
}
var f = Array.prototype.equals.sort(this.concat());
c = Array.prototype.equals.sort(c.concat(),f)
if (reference){
for (var e = 0; e < d; e++) {
if (f[e] != c[e] && !(Array.prototype.equals.NaN && f[e] != f[e] && c[e] != c[e])) {
return false;
}
}
} else {
for (var e = 0; e < d; e++) {
if (!f[e].equals(c[e])) {
return false;
}
}
}
return true;
}
});
Array.prototype.equals.NaN = false; //Set to true to allow [NaN].equals([NaN]) //true
Object.defineProperty(Array.prototype.equals,"sort",{
enumerable:false,
value:function sort (curr,prev) {
var weight = {
"[object Undefined]":6,
"[object Object]":5,
"[object Null]":4,
"[object String]":3,
"[object Number]":2,
"[object Boolean]":1
}
if (prev) { //mark the objects
for (var i = prev.length,j,t;i>0;i--) {
t = typeof (j = prev[i]);
if (j != null && t === "object") {
j._pos = i;
} else if (t !== "object" && t != "undefined" ) break;
}
}
curr.sort (sorter);
if (prev) {
for (var k = prev.length,l,t;k>0;k--) {
t = typeof (l = prev[k]);
if (t === "object" && l != null) {
delete l._pos;
} else if (t !== "object" && t != "undefined" ) break;
}
}
return curr;
function sorter (a,b) {
var tStr = Object.prototype.toString
var types = [tStr.call(a),tStr.call(b)]
var ret = [0,0];
if (types[0] === types[1] && types[0] === "[object Object]") {
if (prev) return a._pos - b._pos
else {
return a === b ? 0 : 1;
}
} else if (types [0] !== types [1]){
return weight[types[0]] - weight[types[1]]
}
return a>b?1:a<b?-1:0;
}
}
});
With this we can reduce the sameElements function to
function sameElements(c, d,referenceCheck) {
return c.equals(d,referenceCheck); //call .equals of Array.prototype.
}
Note. of course you could put all equal functions into the sameElements function, for the cost of the typechecking.
Now here are 3 examples: 1 with deep checking, 2 with reference checking.
var foo = {
a: 1,
obj: {
number: 2,
bool: true,
string: "asd"
},
arr: [1, 2, 3]
};
var bar = {
a: 1,
obj: {
number: 2,
bool: true,
string: "asd"
},
arr: [1, 2, 3]
};
var foobar = {
a: 1,
obj: {
number: 2,
bool: true,
string: "asd"
},
arr: [1, 2, 3, 4]
};
var a = [1, 2, foo, 2, bar, 2];
var b = [foo, 2, 2, 2, bar, 1];
var c = [bar, 2, 2, 2, bar, 1];
So these are the Arrays we compare. And the output is
Check a and b with references only.
console.log (sameElements ( a,b,true)) //true As they contain the same elements
Check b and c with references only
console.log (sameElements (b,c,true)) //false as c contains bar twice.
Check b and c deeply
console.log (sameElements (b,c,false)) //true as bar and foo are equal but not the same
Check for 2 Arrays containing NaN
Array.prototype.equals.NaN = true;
console.log(sameElements([NaN],[NaN],true)); //true.
Array.prototype.equals.NaN = false;
Demo on JSFiddle

You can implement the following algorithm:
If a and b do not have the same length:
Return false.
Otherwise:
Clone b,
For each item in a:
If the item exists in our clone of b:
Remove the item from our clone of b,
Otherwise:
Return false.
Return true.
With Javascript 1.6, you can use every() and indexOf() to write:
function sameElements(a, b)
{
if (a.length != b.length) {
return false;
}
var ourB = b.concat();
return a.every(function(item) {
var index = ourB.indexOf(item);
if (index < 0) {
return false;
} else {
ourB.splice(index, 1);
return true;
}
});
}
Note this implementation does not completely fulfill your requirements because indexOf() uses strict equality (===) internally. If you really want non-strict equality (==), you will have to write an inner loop instead.

Like this perhaps?
var foo = {}; var bar=[];
var a = [3,2,1,foo]; var b = [foo,1,2,3];
function comp(a,b)
{
// immediately discard if they are of different sizes
if (a.length != b.length) return false;
b = b.slice(0); // clone to keep original values after the function
a.forEach(function(e) {
var i;
if ((i = b.indexOf(e)) != -1)
b.splice(i, 1);
});
return !b.length;
}
comp(a,b);

UPDATE
As #Bergi and #thg435 point out my previous implementation was flawed so here is another implementation:
function sameElements(a, b) {
var objs = [];
// if length is not the same then must not be equal
if (a.length != b.length) return false;
// do an initial sort which will group types
a.sort();
b.sort();
for ( var i = 0; i < a.length; i++ ) {
var aIsPrimitive = isPrimitive(a[i]);
var bIsPrimitive = isPrimitive(b[i]);
// NaN will not equal itself
if( a[i] !== a[i] ) {
if( b[i] === b[i] ) {
return false;
}
}
else if (aIsPrimitive && bIsPrimitive) {
if( a[i] != b[i] ) return false;
}
// if not primitive increment the __count property
else if (!aIsPrimitive && !bIsPrimitive) {
incrementCountA(a[i]);
incrementCountB(b[i]);
// keep track on non-primitive objects
objs.push(i);
}
// if both types are not the same then this array
// contains different number of primitives
else {
return false;
}
}
var result = true;
for (var i = 0; i < objs.length; i++) {
var ind = objs[i];
// if __aCount and __bCount match then object exists same
// number of times in both arrays
if( a[ind].__aCount !== a[ind].__bCount ) result = false;
if( b[ind].__aCount !== b[ind].__bCount ) result = false;
// revert object to what it was
// before entering this function
delete a[ind].__aCount;
delete a[ind].__bCount;
delete b[ind].__aCount;
delete b[ind].__bCount;
}
return result;
}
// inspired by #Bergi's code
function isPrimitive(arg) {
return Object(arg) !== arg;
}
function incrementCountA(arg) {
if (arg.hasOwnProperty("__aCount")) {
arg.__aCount = arg.__aCount + 1;
} else {
Object.defineProperty(arg, "__aCount", {
enumerable: false,
value: 1,
writable: true,
configurable: true
});
}
}
function incrementCountB(arg) {
if (arg.hasOwnProperty("__bCount")) {
arg.__bCount = arg.__bCount + 1;
} else {
Object.defineProperty(arg, "__bCount", {
enumerable: false,
value: 1,
writable: true,
configurable: true
});
}
}
Then just call the function
sameElements( ["NaN"], [NaN] ); // false
// As "1" == 1 returns true
sameElements( [1],["1"] ); // true
sameElements( [1,2], [1,2,3] ); //false
The above implement actually defines a new property called "__count" that is used to keep track of non-primitive elements in both arrays. These are deleted before the function returns so as to leave the array elements as before.
Fiddle here
jsperf here.
The reason I changed the jsperf test case was that as #Bergi states the test arrays, especially the fact there were only 2 unique objects in the whole array is not representative of what we are testing for.
One other advantage of this implementation is that if you need to make it compatible with pre IE9 browsers instead of using the defineProperty to create a non-enumerable property you could just use a normal property.

Thanks everyone for sharing ideas! I've came up with the following
function sameElements(a, b) {
var hash = function(x) {
return typeof x + (typeof x == "object" ? a.indexOf(x) : x);
}
return a.map(hash).sort().join() == b.map(hash).sort().join();
}
This isn't the fastest solution, but IMO, most readable one so far.

i wasn't sure if "===" is ok, the question is a bit vauge...
if so, this is quite a bit faster and simpler than some other possible ways of doing it:
function isSame(a,b){
return a.length==b.length &&
a.filter(function(a){ return b.indexOf(a)!==-1 }).length == b.length;
}

Edit 2
1) Thanks to user2357112 for pointing out the Object.prototype.toString.call issue
this also showed, the reason it was that fast, that it didn't consider Arrays ...
I fixed the code,it should be working now :), unfortunately its now at about 59ops/s on chrome and 45ops/s on ff.
Fiddle and JSPerf is updated.
Edit
1)
I fixed the code, it supports mutliple variables referencing the same Object now.
A little bit slower than before, but still over 100ops/s on chrome.
2)
I tried using a bitmask instead of an array to keep multiple positions of the objects, but its nearly 15ops/s slow
3) As pointed ot in the comments i forgot to reset tmp after [[get]] is called fixed the code, the fiddle, and the perf.
So thanks to user2357112 with his Answer heres another approach using counting
var sameElements = (function () {
var f, of, objectFlagName;
of = objectFlagName = "__pos";
var tstr = function (o) {
var t = typeof o;
if (o === null)
t = "null";
return t
};
var types = {};
(function () {
var tmp = {};
Object.defineProperty(types, tstr(1), {
set: function (v) {
if (f)
tmp[v] = -~tmp[v];
else
tmp[v] = ~-tmp[v];
},
get: function () {
var ret = 1;
for (var k in tmp) {
ret &= !tmp[k];
}
tmp = {};
return ret;
}
});
})();
(function () {
var tmp = {};
Object.defineProperty(types, tstr(""), {
set: function (v) {
if (f) {
tmp[v] = -~tmp[v];
} else {
tmp[v] = ~-tmp[v];
}
},
get: function () {
var ret = 1;
for (var k in tmp) {
ret &= !tmp[k];
}
tmp = {};
return ret;
}
});
})();
(function () {
var tmp = [];
function add (v) {
tmp.push(v);
if (v[of]===undefined) {
v[of] = [tmp.length -1];
} else {
v[of].push(tmp.length -1)
}
}
Object.defineProperty(types, tstr({}), {
get: function () {
var ret = true;
for (var i = tmp.length - 1; i >= 0; i--) {
var c = tmp[i]
if (typeof c !== "undefined") {
ret = false
delete c[of]
}
}
tmp = [];
return ret;
},
set: function (v) {
var pos;
if (f) {
add (v);
} else if (!f && (pos = v[of]) !== void 0) {
tmp[pos.pop()] = undefined;
if (pos.length === 0)
delete v[of];
} else {
add (v);
}
}
});
}());
(function () {
var tmp = 0;
Object.defineProperty(types, tstr(undefined), {
get: function () {
var ret = !tmp;
tmp = 0;
return ret;
},
set: function () {
tmp += f ? 1 : -1;
}
});
})();
(function () {
var tmp = 0;
Object.defineProperty(types, tstr(null), {
get: function () {
var ret = !tmp;
tmp = 0;
return ret;
},
set: function () {
tmp += f ? 1 : -1;
}
});
})();
var tIt = [tstr(1), tstr(""), tstr({}), tstr(undefined), tstr(null)];
return function eq(a, b) {
f = true;
for (var i = a.length - 1; i >= 0; i--) {
var v = a[i];
types[tstr(v)] = v;
}
f = false;
for (var k = b.length - 1; k >= 0; k--) {
var w = b[k];
types[tstr(w)] = w;
}
var r = 1;
for (var l = 0, j; j = tIt[l]; l++) {
r &= types [j]
}
return !!r;
}
})()
Here is a JSFiddle and a JSPerf (it uses the same Arrays a and b as in the previous answers perf) with this code vs the Closure compiled
Heres the output. note: it doesn't support a deep comparison anymore, as is
var foo = {a:2}
var bar = {a:1};
var a = [1, 2, foo, 2, bar, 2];
var b = [foo, 2, 2, 2, bar, 1];
var c = [bar, 2, 2, 2, bar, 1];
console.log(sameElements([NaN],[NaN])); //true
console.log (sameElements ( a,b)) //true
console.log (sameElements (b,c)) //false

Using efficient lookup tables for the counts of the elements:
function sameElements(a) { // can compare any number of arrays
var map, maps = [], // counting booleans, numbers and strings
nulls = [], // counting undefined and null
nans = [], // counting nans
objs, counts, objects = [],
al = arguments.length;
// quick escapes:
if (al < 2)
return true;
var l0 = a.length;
if ([].slice.call(arguments).some(function(s) { return s.length != l0; }))
return false;
for (var i=0; i<al; i++) {
var multiset = arguments[i];
maps.push(map = {}); // better: Object.create(null);
objects.push({vals: objs=[], count: counts=[]});
nulls[i] = 0;
nans[i] = 0;
for (var j=0; j<l0; j++) {
var val = multiset[j];
if (val !== val)
nans[i]++;
else if (val === null)
nulls[i]++;
else if (Object(val) === val) { // non-primitive
var ind = objs.indexOf(val);
if (ind > -1)
counts[ind]++;
else
objs.push(val), counts.push(1);
} else { // booleans, strings and numbers do compare together
if (typeof val == "boolean")
val = +val;
if (val in map)
map[val]++;
else
map[val] = 1;
}
}
}
// testing if nulls and nans are the same everywhere
for (var i=1; i<al; i++)
if (nulls[i] != nulls[0] || nans[i] != nans[0])
return false;
// testing if primitives were the same everywhere
var map0 = maps[0];
for (var el in map0)
for (var i=1; i<al; i++) {
if (map0[el] !== maps[i][el])
return false;
delete maps[i][el];
}
for (var i=1; i<al; i++)
for (var el in maps[i])
return false;
// testing if objects were the same everywhere
var objs0 = objects[0].vals,
ol = objs0.length;
counts0 = objects[0].count;
for (var i=1; i<al; i++)
if (objects[i].count.length != ol)
return false;
for (var i=0; i<ol; i++)
for (var j=1; j<al; j++)
if (objects[j].count[ objects[j].vals.indexOf(objs0[i]) ] != counts0[i])
return false;
// else, the multisets are equal:
return true;
}
It still uses indexOf search amongst all objects, so if you have multisets with many different objects you might want to optimize that part as well. Have a look at Unique ID or object signature (and it's duplicate questions) for how to get lookup table keys for them. And if you don't have many primitive values in the multisets, you might just store them in arrays and sort those before comparing each item-by-item (like #Bruno did).
Disclaimer: This solution doesn't try to get the [[PrimitiveValue]] of objects, they will never be counted as equal to primitives (while == would do).
Here is the update on #Bruno's jsperf test of the answers, yet I guess only two objects (each of them present 500 times in the 10k array) and no duplicate primitive values are not representative.

Related

Compare two objects based on property names [duplicate]

I am using node, mocha, and chai for my application. I want to test that my returned results data property is the same "type of object" as one of my model objects (Very similar to chai's instance). I just want to confirm that the two objects have the same sets of property names. I am specifically not interested in the actual values of the properties.
Let's say I have the model Person like below. I want to check that my results.data has all the same properties as the expected model does. So in this case, Person which has a firstName and lastName.
So if results.data.lastName and results.data.firstName both exist, then it should return true. If either one doesn't exist, it should return false. A bonus would be if results.data has any additional properties like results.data.surname, then it would return false because surname doesn't exist in Person.
This model
function Person(data) {
var self = this;
self.firstName = "unknown";
self.lastName = "unknown";
if (typeof data != "undefined") {
self.firstName = data.firstName;
self.lastName = data.lastName;
}
}
You can serialize simple data to check for equality:
data1 = {firstName: 'John', lastName: 'Smith'};
data2 = {firstName: 'Jane', lastName: 'Smith'};
JSON.stringify(data1) === JSON.stringify(data2)
This will give you something like
'{firstName:"John",lastName:"Smith"}' === '{firstName:"Jane",lastName:"Smith"}'
As a function...
function compare(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
compare(data1, data2);
EDIT
If you're using chai like you say, check out http://chaijs.com/api/bdd/#equal-section
EDIT 2
If you just want to check keys...
function compareKeys(a, b) {
var aKeys = Object.keys(a).sort();
var bKeys = Object.keys(b).sort();
return JSON.stringify(aKeys) === JSON.stringify(bKeys);
}
should do it.
2 Here a short ES6 variadic version:
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), []);
const union = new Set(allKeys);
return objects.every(object => union.size === Object.keys(object).length);
}
A little performance test (MacBook Pro - 2,8 GHz Intel Core i7, Node 5.5.0):
var x = {};
var y = {};
for (var i = 0; i < 5000000; ++i) {
x[i] = i;
y[i] = i;
}
Results:
objectsHaveSameKeys(x, y) // took 4996 milliseconds
compareKeys(x, y) // took 14880 milliseconds
hasSameProps(x,y) // after 10 minutes I stopped execution
If you want to check if both objects have the same properties name, you can do this:
function hasSameProps( obj1, obj2 ) {
return Object.keys( obj1 ).every( function( prop ) {
return obj2.hasOwnProperty( prop );
});
}
var obj1 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] },
obj2 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] };
console.log(hasSameProps(obj1, obj2));
In this way you are sure to check only iterable and accessible properties of both the objects.
EDIT - 2013.04.26:
The previous function can be rewritten in the following way:
function hasSameProps( obj1, obj2 ) {
var obj1Props = Object.keys( obj1 ),
obj2Props = Object.keys( obj2 );
if ( obj1Props.length == obj2Props.length ) {
return obj1Props.every( function( prop ) {
return obj2Props.indexOf( prop ) >= 0;
});
}
return false;
}
In this way we check that both the objects have the same number of properties (otherwise the objects haven't the same properties, and we must return a logical false) then, if the number matches, we go to check if they have the same properties.
Bonus
A possible enhancement could be to introduce also a type checking to enforce the match on every property.
If you want deep validation like #speculees, here's an answer using deep-keys (disclosure: I'm sort of a maintainer of this small package)
// obj1 should have all of obj2's properties
var deepKeys = require('deep-keys');
var _ = require('underscore');
assert(0 === _.difference(deepKeys(obj2), deepKeys(obj1)).length);
// obj1 should have exactly obj2's properties
var deepKeys = require('deep-keys');
var _ = require('lodash');
assert(0 === _.xor(deepKeys(obj2), deepKeys(obj1)).length);
or with chai:
var expect = require('chai').expect;
var deepKeys = require('deep-keys');
// obj1 should have all of obj2's properties
expect(deepKeys(obj1)).to.include.members(deepKeys(obj2));
// obj1 should have exactly obj2's properties
expect(deepKeys(obj1)).to.have.members(deepKeys(obj2));
Here's a deep-check version of the function provided above by schirrmacher.
Below is my attempt. Please note:
Solution does not check for null and is not bullet proof
I haven't performance tested it. Maybe schirrmacher or OP can do that and share for the community.
I'm not a JS expert :).
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), [])
const union = new Set(allKeys)
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Update 1
A 90% improvement on the recursive deep-check version is achieved on my computer by skipping the concat() and adding the keys directly to the Set(). The same optimization to the original single level version by schirrmacher also achieves ~40% improvement.
The optimized deep-check is now very similar in performance to the optimized single level version!
function objectsHaveSameKeysOptimized(...objects) {
let union = new Set();
union = objects.reduce((keys, object) => keys.add(Object.keys(object)), union);
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Performance Comparison
var x = {}
var y = {}
var a = {}
for (var j = 0; j < 10; ++j){
a[j] = j
}
for (var i = 0; i < 500000; ++i) {
x[i] = JSON.parse(JSON.stringify(a))
y[i] = JSON.parse(JSON.stringify(a))
}
let startTs = new Date()
let result = objectsHaveSameKeys(x, y)
let endTs = new Date()
console.log('objectsHaveSameKeys = ' + (endTs - startTs)/1000)
Results
A: Recursive/deep-check versions*
objectsHaveSameKeys = 5.185
objectsHaveSameKeysOptimized = 0.415
B: Original non-deep versions
objectsHaveSameKeysOriginalNonDeep = 0.517
objectsHaveSameKeysOriginalNonDeepOptimized = 0.342
function getObjectProperties(object, propertiesString = '') {
let auxPropertiesString = propertiesString;
for (const objectLevel of Object.keys(object).sort((a, b) => a.localeCompare(b))) {
if (typeof object[objectLevel] === 'object') {
auxPropertiesString += getObjectProperties(object[objectLevel], auxPropertiesString);
} else {
auxPropertiesString += objectLevel;
}
}
return auxPropertiesString;
}
function objectsHaveTheSameKeys(objects) {
const properties = [];
for (const object of objects) {
properties.push(getObjectProperties(object));
}
return properties.every(eachProperty => eachProperty === properties[0]);
}
It's a bit rudimentary, but should do the work in case you want to compare properties.
Legacy Browser Object Compare Function
Unlike the other solutions posted here, my Object Compare Function works in ALL BROWSERS, modern or legacy, including very old browsers, even Internet Explorer 5 (c.2000)!
Features:
Can compare an unlimited list of Objects. All must match or fails!
Ignores property order
Only compares "own" properties (i.e. non-prototype)
Matches BOTH property names and property values (key-value pairs)!
Matches functions signatures in objects!
Every object submitted is cross-compared with each other to detect missing properties in cases where one is missing but not in the other
Avoids null, undefined, NaN, Arrays, non-Objects, etc.
{} empty object detection
Works in almost ALL BROWSERS, including even Internet Explorer 5 and many other legacy browsers!
Note the function does not detect complex objects in properties, but you could rewrite the function to call them recursively.
Just call the method with as many objects as you like!
ObjectCompare(myObject1,myObject2,myObject3)
function ObjectCompare() {
try {
if (arguments && arguments.length > 0) {
var len = arguments.length;
if (len > 1) {
var array = [];
for (var i = 0; i < len; i++) {
if (
((typeof arguments[i] !== 'undefined') || (typeof arguments[i] === 'undefined' && arguments[i] !== undefined))
&& (arguments[i] !== null)
&& !(arguments[i] instanceof Array)
&& ((typeof arguments[i] === 'object') || (arguments[i] instanceof Object))
) {
array.push(arguments[i]);
}
}
if (array.length > 1) {
var a1 = array.slice();
var a2 = array.slice();
var len1 = a1.length;
var len2 = a2.length;
var noKeys = true;
var allKeysMatch = true;
for (var x = 0; x < len1; x++) {
console.log('---------- Start Object Check ---------');
//if (len2>0) {
// a2.shift();// remove next item
//}
len2 = a2.length;
if (len2 > 0 && allKeysMatch) {
for (var y = 0; y < len2; y++) {
if (x !== y) {// ignore objects checking themselves
//console.log('Object1: ' + JSON.stringify(a1[x]));
//console.log('Object2: ' + JSON.stringify(a2[y]));
console.log('Object1: ' + a1[x].toString());
console.log('Object2: ' + a2[y].toString());
var ownKeyCount1 = 0;
for (var key1 in a1[x]) {
if (a1[x].hasOwnProperty(key1)) {
// ---------- valid property to check ----------
ownKeyCount1++;
noKeys = false;
allKeysMatch = false;// prove all keys match!
var ownKeyCount2 = 0;
for (var key2 in a2[y]) {
if (a2[y].hasOwnProperty(key2) && !allKeysMatch) {
ownKeyCount2++;
if (key1 !== key1 && key2 !== key2) {// NaN check
allKeysMatch = true;// proven
break;
} else if (key1 === key2) {
if (a1[x][key1].toString() === a2[y][key2].toString()) {
allKeysMatch = true;// proven
console.log('KeyValueMatch=true : ' + key1 + ':' + a1[x][key1] + ' | ' + key2 + ':' + a2[y][key2]);
break;
}
}
}
}
if (ownKeyCount2 === 0) {// if second objects has no keys end early
console.log('-------------- End Check -------------');
return false;
}
// ---------------------------------------------
}
}
console.log('-------------- End Check -------------');
}
}
}
}
console.log('---------------------------------------');
if (noKeys || allKeysMatch) {
// If no keys in any objects, assume all objects are {} empty and so the same.
// If all keys match without errors, then all object match.
return true;
} else {
return false;
}
}
}
console.log('---------------------------------------');
return true;// one object
}
console.log('---------------------------------------');
return false;// no objects
} catch (e) {
if (typeof console !== 'undefined' && console.error) {
console.error('ERROR : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.warn) {
console.warn('WARNING : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.log) {
console.log('ERROR : Function ObjectCompare() : ' + e);
}
return false;
}
}
// TESTING...
var myObject1 = new Object({test: 1, item: 'hello', name: 'john', f: function(){var x=1;}});
var myObject2 = new Object({item: 'hello', name: 'john', test: 1, f: function(){var x=1;}});
var myObject3 = new Object({name: 'john', test: 1, item: 'hello', f: function(){var x=1;}});
// RETURNS TRUE
//console.log('DO ALL OBJECTS MATCH? ' + ObjectCompare(myObject1, myObject2, myObject3));
If you are using underscoreJs then you can simply use _.isEqual function
and it compares all keys and values at each and every level of hierarchy like below example.
var object = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
var object1 = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
console.log(_.isEqual(object, object1));//return true
If all the keys and values for those keys are same in both the objects then it will return true, otherwise return false.
Here is my attempt at validating JSON properties. I used #casey-foster 's approach, but added recursion for deeper validation. The third parameter in function is optional and only used for testing.
//compare json2 to json1
function isValidJson(json1, json2, showInConsole) {
if (!showInConsole)
showInConsole = false;
var aKeys = Object.keys(json1).sort();
var bKeys = Object.keys(json2).sort();
for (var i = 0; i < aKeys.length; i++) {
if (showInConsole)
console.log("---------" + JSON.stringify(aKeys[i]) + " " + JSON.stringify(bKeys[i]))
if (JSON.stringify(aKeys[i]) === JSON.stringify(bKeys[i])) {
if (typeof json1[aKeys[i]] === 'object'){ // contains another obj
if (showInConsole)
console.log("Entering " + JSON.stringify(aKeys[i]))
if (!isValidJson(json1[aKeys[i]], json2[bKeys[i]], showInConsole))
return false; // if recursive validation fails
if (showInConsole)
console.log("Leaving " + JSON.stringify(aKeys[i]))
}
} else {
console.warn("validation failed at " + aKeys[i]);
return false; // if attribute names dont mactch
}
}
return true;
}

Multiple conditions for one variable in 'if' condition. Javascript [duplicate]

This question already has answers here:
How do I check if an array includes a value in JavaScript?
(60 answers)
Closed 6 years ago.
I need to determine if a value exists in an array.
I am using the following function:
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] == obj) {
return true;
}
}
return false;
}
The above function always returns false.
The array values and the function call is as below:
arrValues = ["Sam","Great", "Sample", "High"]
alert(arrValues.contains("Sam"));
jQuery has a utility function for this:
$.inArray(value, array)
Returns index of value in array. Returns -1 if array does not contain value.
See also How do I check if an array includes an object in JavaScript?
var contains = function(needle) {
// Per spec, the way to identify NaN is that it is not equal to itself
var findNaN = needle !== needle;
var indexOf;
if(!findNaN && typeof Array.prototype.indexOf === 'function') {
indexOf = Array.prototype.indexOf;
} else {
indexOf = function(needle) {
var i = -1, index = -1;
for(i = 0; i < this.length; i++) {
var item = this[i];
if((findNaN && item !== item) || item === needle) {
index = i;
break;
}
}
return index;
};
}
return indexOf.call(this, needle) > -1;
};
You can use it like this:
var myArray = [0,1,2],
needle = 1,
index = contains.call(myArray, needle); // true
CodePen validation/usage
This is generally what the indexOf() method is for. You would say:
return arrValues.indexOf('Sam') > -1
Array.prototype.includes()
In ES2016, there is Array.prototype.includes().
The includes() method determines whether an array includes a certain element, returning true or false as appropriate.
Example
["Sam", "Great", "Sample", "High"].includes("Sam"); // true
Support
According to kangax and MDN, the following platforms are supported:
Chrome 47
Edge 14
Firefox 43
Opera 34
Safari 9
Node 6
Support can be expanded using Babel (using babel-polyfill) or core-js. MDN also provides a polyfill:
if (![].includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
'use strict';
var O = Object(this);
var len = parseInt(O.length) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1]) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {k = 0;}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
It's almost always safer to use a library like lodash simply because of all the issues with cross-browser compatibilities and efficiency.
Efficiency because you can be guaranteed that at any given time, a hugely popular library like underscore will have the most efficient method of accomplishing a utility function like this.
_.includes([1, 2, 3], 3); // returns true
If you're concerned about the bulk that's being added to your application by including the whole library, know that you can include functionality separately:
var includes = require('lodash/collections/includes');
NOTICE: With older versions of lodash, this was _.contains() rather than _.includes().
Since ECMAScript6, one can use Set:
var myArray = ['A', 'B', 'C'];
var mySet = new Set(myArray);
var hasB = mySet.has('B'); // true
var hasZ = mySet.has('Z'); // false
tl;dr
function includes(k) {
for(var i=0; i < this.length; i++){
if( this[i] === k || ( this[i] !== this[i] && k !== k ) ){
return true;
}
}
return false;
}
Example
function includes(k) {
for(var i=0; i < this.length; i++){
if( this[i] === k || ( this[i] !== this[i] && k !== k ) ){
return true;
}
}
return false;
}
function log(msg){
$('#out').append('<div>' + msg + '</div>');
}
var arr = [1, "2", NaN, true];
arr.includes = includes;
log('var arr = [1, "2", NaN, true];');
log('<br/>');
log('arr.includes(1): ' + arr.includes(1));
log('arr.includes(2): ' + arr.includes(2));
log('arr.includes("2"): ' + arr.includes("2"));
log('arr.includes(NaN): ' + arr.includes(NaN));
log('arr.includes(true): ' + arr.includes(true));
log('arr.includes(false): ' + arr.includes(false));
#out{
font-family:monospace;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id=out></div>
Longer Answer
I know this question isn't really about whether or not to extend built-in objects, but the attempt of the OP and the comments on this answer highlight that debate. My comment from Feb 12, '13 cites an article that outlines this debate really well, however that link broke and I can't edit the original comment because too much time has passed, so I include it here.
If you're looking to extend the built-in Array object with a contains method, probably the best and most responsible way to do this would be to use this polyfill from MDN. (See also this section of the MDN article on Prototypical inheritance, which explains that "The only good reason for extending a built-in prototype is to backport the features of newer JavaScript engines; for example Array.forEach, etc.")
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
'use strict';
var O = Object(this);
var len = parseInt(O.length) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1]) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {k = 0;}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
Don't want strict equality, or want to choose?
function includes(k, strict) {
strict = strict !== false; // default is true
// strict = !!strict; // default is false
for(var i=0; i < this.length; i++){
if( (this[i] === k && strict) ||
(this[i] == k && !strict) ||
(this[i] !== this[i] && k !== k)
) {
return true;
}
}
return false;
}
My little contribution:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
//usage
if(isInArray(my_array, "my_value"))
{
//...
}
If you have access to ECMA 5 you can use the some method.
MDN SOME Method Link
arrValues = ["Sam","Great", "Sample", "High"];
function namePresent(name){
return name === this.toString();
}
// Note:
// namePresent requires .toString() method to coerce primitive value
// i.e. String {0: "S", 1: "a", 2: "m", length: 3, [[PrimitiveValue]]: "Sam"}
// into
// "Sam"
arrValues.some(namePresent, 'Sam');
=> true;
If you have access to ECMA 6 you can use the includes method.
MDN INCLUDES Method Link
arrValues = ["Sam","Great", "Sample", "High"];
arrValues.includes('Sam');
=> true;
Given the implementation of indexOf for IE (as described by eyelidlessness):
Array.prototype.contains = function(obj) {
return this.indexOf(obj) > -1;
};
You can use _.indexOf method or if you don't want to include whole Underscore.js library in your app, you can have a look how they did it and extract necessary code.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, l = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < l; i++) if (array[i] === item) return i;
return -1;
};
Another option would be to use Array.some (if available) in the following way:
Array.prototype.contains = function(obj) {
return this.some( function(e){ return e === obj } );
}
The anonymous function passed to Array.some will return true if and only if there is an element in the array that is identical to obj. Absent such an element, the function will not return true for any of the elements of the array, so Array.some will return false as well.
Wow, there are a lot of great answers to this question.
I didn't see one that takes a reduce approach so I'll add it in:
var searchForValue = 'pig';
var valueIsInArray = ['horse', 'cat', 'dog'].reduce(function(previous, current){
return previous || searchForValue === current ? true : false;
}, false);
console.log('The value "' + searchForValue + '" is in the array: ' + valueIsInArray);
Here's a fiddle of it in action.
The answer provided didn't work for me, but it gave me an idea:
Array.prototype.contains = function(obj)
{
return (this.join(',')).indexOf(obj) > -1;
}
It isn't perfect because items that are the same beyond the groupings could end up matching. Such as my example
var c=[];
var d=[];
function a()
{
var e = '1';
var f = '2';
c[0] = ['1','1'];
c[1] = ['2','2'];
c[2] = ['3','3'];
d[0] = [document.getElementById('g').value,document.getElementById('h').value];
document.getElementById('i').value = c.join(',');
document.getElementById('j').value = d.join(',');
document.getElementById('b').value = c.contains(d);
}
When I call this function with the 'g' and 'h' fields containing 1 and 2 respectively, it still finds it because the resulting string from the join is: 1,1,2,2,3,3
Since it is doubtful in my situation that I will come across this type of situation, I'm using this. I thought I would share incase someone else couldn't make the chosen answer work either.
Using array .map function that executes a function for every value in an array seems cleanest to me.
Ref: Array.prototype.map()
This method can work well both for simple arrays and for arrays of objects where you need to see if a key/value exists in an array of objects.
function inArray(myArray,myValue){
var inArray = false;
myArray.map(function(key){
if (key === myValue){
inArray=true;
}
});
return inArray;
};
var anArray = [2,4,6,8]
console.log(inArray(anArray, 8)); // returns true
console.log(inArray(anArray, 1)); // returns false
function inArrayOfObjects(myArray,myValue,objElement){
var inArray = false;
myArray.map(function(arrayObj){
if (arrayObj[objElement] === myValue) {
inArray=true;
}
});
return inArray;
};
var objArray = [{id:4,value:'foo'},{id:5,value:'bar'}]
console.log(inArrayOfObjects(objArray, 4, 'id')); // returns true
console.log(inArrayOfObjects(objArray, 'bar', 'value')); // returns true
console.log(inArrayOfObjects(objArray, 1, 'id')); // returns false
function setFound(){
var l = arr.length, textBox1 = document.getElementById("text1");
for(var i=0; i<l;i++)
{
if(arr[i]==searchele){
textBox1 .value = "Found";
return;
}
}
textBox1 .value = "Not Found";
return;
}
This program checks whether the given element is found or not. Id
text1 represents id of textbox and searchele represents element to be
searched (got fron user); if you want index, use i value
The simplest solution for a contains function, would be a function that looks like this :
var contains = function (haystack, needle) {
return !!~haystack.indexOf(needle);
}
Ideally, you wouldn't make this a stand-alone function, though, but part of a helper library :
var helper = {};
helper.array = {
contains : function (haystack, needle) {
return !!~haystack.indexOf(needle);
},
...
};
Now, if you happen to be one of those unlucky people who still needs to support IE<9 and thus can't rely on indexOf, you could use this polyfill, which I got from the MDN :
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement, fromIndex) {
var k;
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (len === 0) {
return -1;
}
var n = +fromIndex || 0;
if (Math.abs(n) === Infinity) {
n = 0;
}
if (n >= len) {
return -1;
}
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
while (k < len) {
if (k in o && o[k] === searchElement) {
return k;
}
k++;
}
return -1;
};
}
I prefer simplicity:
var days = [1, 2, 3, 4, 5];
if ( 2 in days ) {console.log('weekday');}

Check value in array [duplicate]

This question already has answers here:
How do I check if an array includes a value in JavaScript?
(60 answers)
Closed 6 years ago.
I need to determine if a value exists in an array.
I am using the following function:
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] == obj) {
return true;
}
}
return false;
}
The above function always returns false.
The array values and the function call is as below:
arrValues = ["Sam","Great", "Sample", "High"]
alert(arrValues.contains("Sam"));
jQuery has a utility function for this:
$.inArray(value, array)
Returns index of value in array. Returns -1 if array does not contain value.
See also How do I check if an array includes an object in JavaScript?
var contains = function(needle) {
// Per spec, the way to identify NaN is that it is not equal to itself
var findNaN = needle !== needle;
var indexOf;
if(!findNaN && typeof Array.prototype.indexOf === 'function') {
indexOf = Array.prototype.indexOf;
} else {
indexOf = function(needle) {
var i = -1, index = -1;
for(i = 0; i < this.length; i++) {
var item = this[i];
if((findNaN && item !== item) || item === needle) {
index = i;
break;
}
}
return index;
};
}
return indexOf.call(this, needle) > -1;
};
You can use it like this:
var myArray = [0,1,2],
needle = 1,
index = contains.call(myArray, needle); // true
CodePen validation/usage
This is generally what the indexOf() method is for. You would say:
return arrValues.indexOf('Sam') > -1
Array.prototype.includes()
In ES2016, there is Array.prototype.includes().
The includes() method determines whether an array includes a certain element, returning true or false as appropriate.
Example
["Sam", "Great", "Sample", "High"].includes("Sam"); // true
Support
According to kangax and MDN, the following platforms are supported:
Chrome 47
Edge 14
Firefox 43
Opera 34
Safari 9
Node 6
Support can be expanded using Babel (using babel-polyfill) or core-js. MDN also provides a polyfill:
if (![].includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
'use strict';
var O = Object(this);
var len = parseInt(O.length) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1]) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {k = 0;}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
It's almost always safer to use a library like lodash simply because of all the issues with cross-browser compatibilities and efficiency.
Efficiency because you can be guaranteed that at any given time, a hugely popular library like underscore will have the most efficient method of accomplishing a utility function like this.
_.includes([1, 2, 3], 3); // returns true
If you're concerned about the bulk that's being added to your application by including the whole library, know that you can include functionality separately:
var includes = require('lodash/collections/includes');
NOTICE: With older versions of lodash, this was _.contains() rather than _.includes().
Since ECMAScript6, one can use Set:
var myArray = ['A', 'B', 'C'];
var mySet = new Set(myArray);
var hasB = mySet.has('B'); // true
var hasZ = mySet.has('Z'); // false
tl;dr
function includes(k) {
for(var i=0; i < this.length; i++){
if( this[i] === k || ( this[i] !== this[i] && k !== k ) ){
return true;
}
}
return false;
}
Example
function includes(k) {
for(var i=0; i < this.length; i++){
if( this[i] === k || ( this[i] !== this[i] && k !== k ) ){
return true;
}
}
return false;
}
function log(msg){
$('#out').append('<div>' + msg + '</div>');
}
var arr = [1, "2", NaN, true];
arr.includes = includes;
log('var arr = [1, "2", NaN, true];');
log('<br/>');
log('arr.includes(1): ' + arr.includes(1));
log('arr.includes(2): ' + arr.includes(2));
log('arr.includes("2"): ' + arr.includes("2"));
log('arr.includes(NaN): ' + arr.includes(NaN));
log('arr.includes(true): ' + arr.includes(true));
log('arr.includes(false): ' + arr.includes(false));
#out{
font-family:monospace;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id=out></div>
Longer Answer
I know this question isn't really about whether or not to extend built-in objects, but the attempt of the OP and the comments on this answer highlight that debate. My comment from Feb 12, '13 cites an article that outlines this debate really well, however that link broke and I can't edit the original comment because too much time has passed, so I include it here.
If you're looking to extend the built-in Array object with a contains method, probably the best and most responsible way to do this would be to use this polyfill from MDN. (See also this section of the MDN article on Prototypical inheritance, which explains that "The only good reason for extending a built-in prototype is to backport the features of newer JavaScript engines; for example Array.forEach, etc.")
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
'use strict';
var O = Object(this);
var len = parseInt(O.length) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1]) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {k = 0;}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
Don't want strict equality, or want to choose?
function includes(k, strict) {
strict = strict !== false; // default is true
// strict = !!strict; // default is false
for(var i=0; i < this.length; i++){
if( (this[i] === k && strict) ||
(this[i] == k && !strict) ||
(this[i] !== this[i] && k !== k)
) {
return true;
}
}
return false;
}
My little contribution:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
//usage
if(isInArray(my_array, "my_value"))
{
//...
}
If you have access to ECMA 5 you can use the some method.
MDN SOME Method Link
arrValues = ["Sam","Great", "Sample", "High"];
function namePresent(name){
return name === this.toString();
}
// Note:
// namePresent requires .toString() method to coerce primitive value
// i.e. String {0: "S", 1: "a", 2: "m", length: 3, [[PrimitiveValue]]: "Sam"}
// into
// "Sam"
arrValues.some(namePresent, 'Sam');
=> true;
If you have access to ECMA 6 you can use the includes method.
MDN INCLUDES Method Link
arrValues = ["Sam","Great", "Sample", "High"];
arrValues.includes('Sam');
=> true;
Given the implementation of indexOf for IE (as described by eyelidlessness):
Array.prototype.contains = function(obj) {
return this.indexOf(obj) > -1;
};
You can use _.indexOf method or if you don't want to include whole Underscore.js library in your app, you can have a look how they did it and extract necessary code.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, l = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < l; i++) if (array[i] === item) return i;
return -1;
};
Another option would be to use Array.some (if available) in the following way:
Array.prototype.contains = function(obj) {
return this.some( function(e){ return e === obj } );
}
The anonymous function passed to Array.some will return true if and only if there is an element in the array that is identical to obj. Absent such an element, the function will not return true for any of the elements of the array, so Array.some will return false as well.
Wow, there are a lot of great answers to this question.
I didn't see one that takes a reduce approach so I'll add it in:
var searchForValue = 'pig';
var valueIsInArray = ['horse', 'cat', 'dog'].reduce(function(previous, current){
return previous || searchForValue === current ? true : false;
}, false);
console.log('The value "' + searchForValue + '" is in the array: ' + valueIsInArray);
Here's a fiddle of it in action.
The answer provided didn't work for me, but it gave me an idea:
Array.prototype.contains = function(obj)
{
return (this.join(',')).indexOf(obj) > -1;
}
It isn't perfect because items that are the same beyond the groupings could end up matching. Such as my example
var c=[];
var d=[];
function a()
{
var e = '1';
var f = '2';
c[0] = ['1','1'];
c[1] = ['2','2'];
c[2] = ['3','3'];
d[0] = [document.getElementById('g').value,document.getElementById('h').value];
document.getElementById('i').value = c.join(',');
document.getElementById('j').value = d.join(',');
document.getElementById('b').value = c.contains(d);
}
When I call this function with the 'g' and 'h' fields containing 1 and 2 respectively, it still finds it because the resulting string from the join is: 1,1,2,2,3,3
Since it is doubtful in my situation that I will come across this type of situation, I'm using this. I thought I would share incase someone else couldn't make the chosen answer work either.
Using array .map function that executes a function for every value in an array seems cleanest to me.
Ref: Array.prototype.map()
This method can work well both for simple arrays and for arrays of objects where you need to see if a key/value exists in an array of objects.
function inArray(myArray,myValue){
var inArray = false;
myArray.map(function(key){
if (key === myValue){
inArray=true;
}
});
return inArray;
};
var anArray = [2,4,6,8]
console.log(inArray(anArray, 8)); // returns true
console.log(inArray(anArray, 1)); // returns false
function inArrayOfObjects(myArray,myValue,objElement){
var inArray = false;
myArray.map(function(arrayObj){
if (arrayObj[objElement] === myValue) {
inArray=true;
}
});
return inArray;
};
var objArray = [{id:4,value:'foo'},{id:5,value:'bar'}]
console.log(inArrayOfObjects(objArray, 4, 'id')); // returns true
console.log(inArrayOfObjects(objArray, 'bar', 'value')); // returns true
console.log(inArrayOfObjects(objArray, 1, 'id')); // returns false
function setFound(){
var l = arr.length, textBox1 = document.getElementById("text1");
for(var i=0; i<l;i++)
{
if(arr[i]==searchele){
textBox1 .value = "Found";
return;
}
}
textBox1 .value = "Not Found";
return;
}
This program checks whether the given element is found or not. Id
text1 represents id of textbox and searchele represents element to be
searched (got fron user); if you want index, use i value
The simplest solution for a contains function, would be a function that looks like this :
var contains = function (haystack, needle) {
return !!~haystack.indexOf(needle);
}
Ideally, you wouldn't make this a stand-alone function, though, but part of a helper library :
var helper = {};
helper.array = {
contains : function (haystack, needle) {
return !!~haystack.indexOf(needle);
},
...
};
Now, if you happen to be one of those unlucky people who still needs to support IE<9 and thus can't rely on indexOf, you could use this polyfill, which I got from the MDN :
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement, fromIndex) {
var k;
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (len === 0) {
return -1;
}
var n = +fromIndex || 0;
if (Math.abs(n) === Infinity) {
n = 0;
}
if (n >= len) {
return -1;
}
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
while (k < len) {
if (k in o && o[k] === searchElement) {
return k;
}
k++;
}
return -1;
};
}
I prefer simplicity:
var days = [1, 2, 3, 4, 5];
if ( 2 in days ) {console.log('weekday');}

Implementing contains, which checks where the specified elements exists in the array or not is not working

I have tried this code to find if some object already exists in Array, but, it is not working:
function lote(pnIdLote,psNmLote, pnIdQuadro){
this.idlote = pnIdLote;
this.nmlote = psNmLote;
this.idquadro = pnIdQuadro;
}
Array.prototype.contains = function(obj){
var i = this.length;
while(i--){
console.log(i);
if (this[i] == obj) return true;
}
return false;
};
My code:
$(data).each(function(index){
if(this.idlote!=""){
loLote = new lote(this.idlote, this.nmlote, this.quadro);
if(!laLote.contains(loLote)){
laLote.push(loLote);
}
}
});
console.log(laLote);
The array appears this way:
[
lote
idlote: "2"
idquadro: "1"
nmlote: "Lote 1"
__proto__: lote
,
lote
idlote: "2"
idquadro: "1"
nmlote: "Lote 1"
__proto__: lote
,
lote
idlote: "2"
idquadro: "1"
nmlote: "Lote 1"
__proto__: lote
]
And I'd like an array with only one element, like this:
[
lote
idlote: "2"
idquadro: "1"
nmlote: "Lote 1"
__proto__: lote
]
Where do I fix my code?
Thanks,
Luciano
When a and b are objects in JavaScript, a == b or a === b is only true if a and b point to the same instance.
Example:
var a = [], b = [], c = a;
alert(a == b); // False
alert(a == c); // True
For your specific comparison method, I recommend creating a custom array constructor, to not pollute the Array object:
function lote() {} // Your lote constructor
var customArray = function() {
return Array.apply(this, arguments);
}
customArray.prototype = new Array;
customArray.prototype.contains = function(obj) {
var i = this.length;
// Not designed for non-"lote" instances, return null
if (!(obj instanceof lote)) return null;
while (i--) {
var current = this[i];
if (current.idlote === obj.idlote &&
current.nmlote === obj.nmlote &&
current.idquadro === obj.idquadrio) return true;
}
return false;
}
Instead of hard-coding the property names, you can also loop through the Object's properties, using Object.keys():
customArray.prototype.contains = function(obj) {
var i = this.length;
// Not designed for non-"lote" instances, return null
if (!(obj instanceof lote)) return null;
var keys = Object.keys(obj), keysLen = keys.length;
// Labeled loop
loop: while (i--) {
var current = this[i];
var len = keysLen, key;
for (len--) {
key = keys[len];
if (current[key] !== current[key]) {
len = -1;
continue loop; //Not equal, go to the next item in the outer loop
}
}
return true; // At this point, every property was equal.
}
return false;
}
Note that the shown method depends is based on the properties of the given obj. If it's not a valid lote object, the function returns null. Object.keys retrieves all properties of the given obj.
I think you should change the "contains" method to use Object.equals function, and you could use the code of
Array.prototype.contains = function(obj){
var i = this.length;
while(i--){
console.log(i);
if(this[i].equals(obj)) return true;
}
return false;
};
Object.prototype.equals = function(x){
var p;
for(p in this) {
if(typeof(x[p])=='undefined') {return false;}
}
for(p in this) {
if (this[p]) {
switch(typeof(this[p])) {
case 'object':
if (!this[p].equals(x[p])) { return false; } break;
case 'function':
if (typeof(x[p])=='undefined' ||
(p != 'equals' && this[p].toString() != x[p].toString()))
return false;
break;
default:
if (this[p] != x[p]) { return false; }
}
} else {
if (x[p])
return false;
}
}
for(p in x) {
if(typeof(this[p])=='undefined') {return false;}
}
return true;
}
Code of object comparison extracted from Object comparison in JavaScript

Determine whether an array contains a value [duplicate]

This question already has answers here:
How do I check if an array includes a value in JavaScript?
(60 answers)
Closed 6 years ago.
I need to determine if a value exists in an array.
I am using the following function:
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] == obj) {
return true;
}
}
return false;
}
The above function always returns false.
The array values and the function call is as below:
arrValues = ["Sam","Great", "Sample", "High"]
alert(arrValues.contains("Sam"));
jQuery has a utility function for this:
$.inArray(value, array)
Returns index of value in array. Returns -1 if array does not contain value.
See also How do I check if an array includes an object in JavaScript?
var contains = function(needle) {
// Per spec, the way to identify NaN is that it is not equal to itself
var findNaN = needle !== needle;
var indexOf;
if(!findNaN && typeof Array.prototype.indexOf === 'function') {
indexOf = Array.prototype.indexOf;
} else {
indexOf = function(needle) {
var i = -1, index = -1;
for(i = 0; i < this.length; i++) {
var item = this[i];
if((findNaN && item !== item) || item === needle) {
index = i;
break;
}
}
return index;
};
}
return indexOf.call(this, needle) > -1;
};
You can use it like this:
var myArray = [0,1,2],
needle = 1,
index = contains.call(myArray, needle); // true
CodePen validation/usage
This is generally what the indexOf() method is for. You would say:
return arrValues.indexOf('Sam') > -1
Array.prototype.includes()
In ES2016, there is Array.prototype.includes().
The includes() method determines whether an array includes a certain element, returning true or false as appropriate.
Example
["Sam", "Great", "Sample", "High"].includes("Sam"); // true
Support
According to kangax and MDN, the following platforms are supported:
Chrome 47
Edge 14
Firefox 43
Opera 34
Safari 9
Node 6
Support can be expanded using Babel (using babel-polyfill) or core-js. MDN also provides a polyfill:
if (![].includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
'use strict';
var O = Object(this);
var len = parseInt(O.length) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1]) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {k = 0;}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
It's almost always safer to use a library like lodash simply because of all the issues with cross-browser compatibilities and efficiency.
Efficiency because you can be guaranteed that at any given time, a hugely popular library like underscore will have the most efficient method of accomplishing a utility function like this.
_.includes([1, 2, 3], 3); // returns true
If you're concerned about the bulk that's being added to your application by including the whole library, know that you can include functionality separately:
var includes = require('lodash/collections/includes');
NOTICE: With older versions of lodash, this was _.contains() rather than _.includes().
Since ECMAScript6, one can use Set:
var myArray = ['A', 'B', 'C'];
var mySet = new Set(myArray);
var hasB = mySet.has('B'); // true
var hasZ = mySet.has('Z'); // false
tl;dr
function includes(k) {
for(var i=0; i < this.length; i++){
if( this[i] === k || ( this[i] !== this[i] && k !== k ) ){
return true;
}
}
return false;
}
Example
function includes(k) {
for(var i=0; i < this.length; i++){
if( this[i] === k || ( this[i] !== this[i] && k !== k ) ){
return true;
}
}
return false;
}
function log(msg){
$('#out').append('<div>' + msg + '</div>');
}
var arr = [1, "2", NaN, true];
arr.includes = includes;
log('var arr = [1, "2", NaN, true];');
log('<br/>');
log('arr.includes(1): ' + arr.includes(1));
log('arr.includes(2): ' + arr.includes(2));
log('arr.includes("2"): ' + arr.includes("2"));
log('arr.includes(NaN): ' + arr.includes(NaN));
log('arr.includes(true): ' + arr.includes(true));
log('arr.includes(false): ' + arr.includes(false));
#out{
font-family:monospace;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id=out></div>
Longer Answer
I know this question isn't really about whether or not to extend built-in objects, but the attempt of the OP and the comments on this answer highlight that debate. My comment from Feb 12, '13 cites an article that outlines this debate really well, however that link broke and I can't edit the original comment because too much time has passed, so I include it here.
If you're looking to extend the built-in Array object with a contains method, probably the best and most responsible way to do this would be to use this polyfill from MDN. (See also this section of the MDN article on Prototypical inheritance, which explains that "The only good reason for extending a built-in prototype is to backport the features of newer JavaScript engines; for example Array.forEach, etc.")
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
'use strict';
var O = Object(this);
var len = parseInt(O.length) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1]) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {k = 0;}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
Don't want strict equality, or want to choose?
function includes(k, strict) {
strict = strict !== false; // default is true
// strict = !!strict; // default is false
for(var i=0; i < this.length; i++){
if( (this[i] === k && strict) ||
(this[i] == k && !strict) ||
(this[i] !== this[i] && k !== k)
) {
return true;
}
}
return false;
}
My little contribution:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
//usage
if(isInArray(my_array, "my_value"))
{
//...
}
If you have access to ECMA 5 you can use the some method.
MDN SOME Method Link
arrValues = ["Sam","Great", "Sample", "High"];
function namePresent(name){
return name === this.toString();
}
// Note:
// namePresent requires .toString() method to coerce primitive value
// i.e. String {0: "S", 1: "a", 2: "m", length: 3, [[PrimitiveValue]]: "Sam"}
// into
// "Sam"
arrValues.some(namePresent, 'Sam');
=> true;
If you have access to ECMA 6 you can use the includes method.
MDN INCLUDES Method Link
arrValues = ["Sam","Great", "Sample", "High"];
arrValues.includes('Sam');
=> true;
Given the implementation of indexOf for IE (as described by eyelidlessness):
Array.prototype.contains = function(obj) {
return this.indexOf(obj) > -1;
};
You can use _.indexOf method or if you don't want to include whole Underscore.js library in your app, you can have a look how they did it and extract necessary code.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, l = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < l; i++) if (array[i] === item) return i;
return -1;
};
Another option would be to use Array.some (if available) in the following way:
Array.prototype.contains = function(obj) {
return this.some( function(e){ return e === obj } );
}
The anonymous function passed to Array.some will return true if and only if there is an element in the array that is identical to obj. Absent such an element, the function will not return true for any of the elements of the array, so Array.some will return false as well.
Wow, there are a lot of great answers to this question.
I didn't see one that takes a reduce approach so I'll add it in:
var searchForValue = 'pig';
var valueIsInArray = ['horse', 'cat', 'dog'].reduce(function(previous, current){
return previous || searchForValue === current ? true : false;
}, false);
console.log('The value "' + searchForValue + '" is in the array: ' + valueIsInArray);
Here's a fiddle of it in action.
The answer provided didn't work for me, but it gave me an idea:
Array.prototype.contains = function(obj)
{
return (this.join(',')).indexOf(obj) > -1;
}
It isn't perfect because items that are the same beyond the groupings could end up matching. Such as my example
var c=[];
var d=[];
function a()
{
var e = '1';
var f = '2';
c[0] = ['1','1'];
c[1] = ['2','2'];
c[2] = ['3','3'];
d[0] = [document.getElementById('g').value,document.getElementById('h').value];
document.getElementById('i').value = c.join(',');
document.getElementById('j').value = d.join(',');
document.getElementById('b').value = c.contains(d);
}
When I call this function with the 'g' and 'h' fields containing 1 and 2 respectively, it still finds it because the resulting string from the join is: 1,1,2,2,3,3
Since it is doubtful in my situation that I will come across this type of situation, I'm using this. I thought I would share incase someone else couldn't make the chosen answer work either.
Using array .map function that executes a function for every value in an array seems cleanest to me.
Ref: Array.prototype.map()
This method can work well both for simple arrays and for arrays of objects where you need to see if a key/value exists in an array of objects.
function inArray(myArray,myValue){
var inArray = false;
myArray.map(function(key){
if (key === myValue){
inArray=true;
}
});
return inArray;
};
var anArray = [2,4,6,8]
console.log(inArray(anArray, 8)); // returns true
console.log(inArray(anArray, 1)); // returns false
function inArrayOfObjects(myArray,myValue,objElement){
var inArray = false;
myArray.map(function(arrayObj){
if (arrayObj[objElement] === myValue) {
inArray=true;
}
});
return inArray;
};
var objArray = [{id:4,value:'foo'},{id:5,value:'bar'}]
console.log(inArrayOfObjects(objArray, 4, 'id')); // returns true
console.log(inArrayOfObjects(objArray, 'bar', 'value')); // returns true
console.log(inArrayOfObjects(objArray, 1, 'id')); // returns false
function setFound(){
var l = arr.length, textBox1 = document.getElementById("text1");
for(var i=0; i<l;i++)
{
if(arr[i]==searchele){
textBox1 .value = "Found";
return;
}
}
textBox1 .value = "Not Found";
return;
}
This program checks whether the given element is found or not. Id
text1 represents id of textbox and searchele represents element to be
searched (got fron user); if you want index, use i value
The simplest solution for a contains function, would be a function that looks like this :
var contains = function (haystack, needle) {
return !!~haystack.indexOf(needle);
}
Ideally, you wouldn't make this a stand-alone function, though, but part of a helper library :
var helper = {};
helper.array = {
contains : function (haystack, needle) {
return !!~haystack.indexOf(needle);
},
...
};
Now, if you happen to be one of those unlucky people who still needs to support IE<9 and thus can't rely on indexOf, you could use this polyfill, which I got from the MDN :
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement, fromIndex) {
var k;
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (len === 0) {
return -1;
}
var n = +fromIndex || 0;
if (Math.abs(n) === Infinity) {
n = 0;
}
if (n >= len) {
return -1;
}
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
while (k < len) {
if (k in o && o[k] === searchElement) {
return k;
}
k++;
}
return -1;
};
}
I prefer simplicity:
var days = [1, 2, 3, 4, 5];
if ( 2 in days ) {console.log('weekday');}

Categories