Polyfill for Array.includes checking NaN - javascript

So first off this is not an issue I am facing. I was going through some Javascript blogs when I encountered the Array prototype methods of .indexOf and .includes. So if an array has NaN as a value, then probably indexOf is not going to be able to figure it out and I am left with using .includes. But my question here is, since the browser compatibility of includes actually excludes IE, what should be the alternative to detect the NaN check? I thought of constructing a polyfill by referring this
if (Array.prototype.includes) {
Object.defineProperty(Array.prototype, "includes", {
enumerable: false,
value: function(obj) {
var newArr = this.filter(function(el) {
return el == obj;
});
return newArr.length > 0;
}
});
}
var arr = [NaN];
console.log(arr.includes(NaN));
But unfortunately, it is returning false as well. So what other options do I have? Or am I missing something out?

You can include a polyfill for Number.isNaN as well, and then use it in your filter test - if both the obj and el pass Number.isNaN, then return true:
Number.isNaN = Number.isNaN || function(value) {
return value !== value;
}
// if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, "includes", {
enumerable: false,
value: function(obj) {
var newArr = this.filter(function(el) {
return el == obj || Number.isNaN(el) && Number.isNaN(obj);
});
return newArr.length > 0;
}
});
// }
var arr = [NaN];
console.log(arr.includes(NaN));

Array#includes uses the Same-Value-Zero algorithm, which is not the same as ==.
Same-Value is provided by Object.is(), and you can manually check for -0 and +0 to get the "-Zero" part of the check.
The linked page includes a polyfill, although since the polyfill includes a step to make -0 and +0 different - which you don't want in a Same-Value-Zero algorithm - you can just leave it out and simplify accordingly:
function SameValueZero(x, y) {
return x === y || (x !== x && y !== y);
}

You can find the index of NaN using firstIndex. try like this.
var arr = [NaN];
let index = arr.findIndex(Number.isNaN)
console.log(index >= 0);

Related

Parsing JSON value fields is array or not. ( Javascript) [duplicate]

This question already has answers here:
How do I check if a variable is an array in JavaScript?
(24 answers)
Closed 1 year ago.
I'm trying to write a function that either accepts a list of strings, or a single string. If it's a string, then I want to convert it to an array with just the one item so I can loop over it without fear of an error.
So how do I check if the variable is an array?
The method given in the ECMAScript standard to find the class of Object is to use the toString method from Object.prototype.
if(Object.prototype.toString.call(someVar) === '[object Array]') {
alert('Array!');
}
Or you could use typeof to test if it is a string:
if(typeof someVar === 'string') {
someVar = [someVar];
}
Or if you're not concerned about performance, you could just do a concat to a new empty Array.
someVar = [].concat(someVar);
There's also the constructor which you can query directly:
if (somevar.constructor.name == "Array") {
// do something
}
Check out a thorough treatment from T.J. Crowder's blog, as posted in his comment below.
Check out this benchmark to get an idea which method performs better: http://jsben.ch/#/QgYAV
From #Bharath, convert a string to an array using ES6 for the question asked:
const convertStringToArray = (object) => {
return (typeof object === 'string') ? Array(object) : object
}
Suppose:
let m = 'bla'
let n = ['bla','Meow']
let y = convertStringToArray(m)
let z = convertStringToArray(n)
console.log('check y: '+JSON.stringify(y)) . // check y: ['bla']
console.log('check y: '+JSON.stringify(z)) . // check y: ['bla','Meow']
In modern browsers you can do:
Array.isArray(obj)
(Supported by Chrome 5, Firefox 4.0, Internet Explorer 9, Opera 10.5 and Safari 5)
For backward compatibility you can add the following:
// Only implement if no native implementation is available
if (typeof Array.isArray === 'undefined') {
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
};
If you use jQuery you can use jQuery.isArray(obj) or $.isArray(obj). If you use Underscore.js you can use _.isArray(obj).
If you don't need to detect arrays created in different frames you can also just use instanceof:
obj instanceof Array
I would first check if your implementation supports isArray:
if (Array.isArray)
return Array.isArray(v);
You could also try using the instanceof operator
v instanceof Array
jQuery also offers an $.isArray() method:
var a = ["A", "AA", "AAA"];
if($.isArray(a)) {
alert("a is an array!");
} else {
alert("a is not an array!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This is the fastest among all methods (all browsers supported):
function isArray(obj){
return !!obj && obj.constructor === Array;
}
Imagine you have this array below:
var arr = [1,2,3,4,5];
JavaScript (new and older browsers):
function isArray(arr) {
return arr.constructor.toString().indexOf("Array") > -1;
}
or
function isArray(arr) {
return arr instanceof Array;
}
or
function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
Then call it like this:
isArray(arr);
JavaScript (Internet Explorer 9+, Chrome 5+, Firefox 4+, Safari 5+, and Opera 10.5+)
Array.isArray(arr);
jQuery:
$.isArray(arr);
Angular:
angular.isArray(arr);
Underscore.js and Lodash:
_.isArray(arr);
Array.isArray works fast, but it isn't supported by all versions of browsers.
So you could make an exception for others and use a universal method:
Utils = {};
Utils.isArray = ('isArray' in Array) ?
Array.isArray :
function (value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
A simple function to check this:
function isArray(object)
{
return object.constructor === Array;
}
You can use Array.isArray(). Here is a polyfill:
if (Array.isArray == null) {
Array.isArray = (arr) => Object.prototype.toString.call(arr) === "[object Array]"
}
As MDN says in here:
use Array.isArray or Object.prototype.toString.call to differentiate
regular objects from arrays
Like this:
Object.prototype.toString.call(arr) === '[object Array]', or
Array.isArray(arr)
There's just one line solution for this question
x instanceof Array
where x is the variable it will return true if x is an array and false if it is not.
I would make a function to test the type of object you are dealing with...
function whatAmI(me){ return Object.prototype.toString.call(me).split(/\W/)[2]; }
// tests
console.log(
whatAmI(["aiming","#"]),
whatAmI({living:4,breathing:4}),
whatAmI(function(ing){ return ing+" to the global window" }),
whatAmI("going to do with you?")
);
// output: Array Object Function String
then you can write a simple if statement...
if(whatAmI(myVar) === "Array"){
// do array stuff
} else { // could also check `if(whatAmI(myVar) === "String")` here to be sure
// do string stuff
}
You can check the type of your variable whether it is an array with;
var myArray=[];
if(myArray instanceof Array)
{
....
}
I do this in a very simple way. It works for me.
Array.prototype.isArray = true;
a=[]; b={};
a.isArray // true
b.isArray // (undefined -> false)
This is my attempt to improve on this answer taking into account the comments:
var isArray = myArray && myArray.constructor === Array;
It gets rid of the if/else, and accounts for the possibility of the array being null or undefined
I have updated the jsperf fiddle with two alternative methods as well as error checking.
It turns out that the method defining a constant value in the 'Object' and 'Array' prototypes is faster than any of the other methods. It is a somewhat surprising result.
/* Initialisation */
Object.prototype.isArray = function() {
return false;
};
Array.prototype.isArray = function() {
return true;
};
Object.prototype._isArray = false;
Array.prototype._isArray = true;
var arr = ["1", "2"];
var noarr = "1";
/* Method 1 (function) */
if (arr.isArray()) document.write("arr is an array according to function<br/>");
if (!noarr.isArray()) document.write("noarr is not an array according to function<br/>");
/* Method 2 (value) - **** FASTEST ***** */
if (arr._isArray) document.write("arr is an array according to member value<br/>");
if (!noarr._isArray) document.write("noarr is not an array according to member value<br/>");
These two methods do not work if the variable takes the undefined value, but they do work if you are certain that they have a value. With regards to checking with performance in mind if a value is an array or a single value, the second method looks like a valid fast method. It is slightly faster than 'instanceof' on Chrome, twice as fast as the second best method in Internet Explorer, Opera and Safari (on my machine).
I know, that people are looking for some kind of raw JavaScript approach. But if you want think less about it, take a look at Underscore.js' isArray:
_.isArray(object)
It returns true if object is an Array.
(function(){ return _.isArray(arguments); })();
=> false
_.isArray([1,2,3]);
=> true
The best practice is to compare it using constructor, something like this
if(some_variable.constructor === Array){
// do something
}
You can use other methods too, like typeOf, converting it to a string and then comparing, but comparing it with dataType is always a better approach.
The best solution I've seen is a cross-browser replacement for typeof. Check Angus Croll's solution.
The TL;DR version is below, but the article is a great discussion of the issue so you should read it if you have time.
Object.toType = function(obj) {
return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
}
// ... and usage:
Object.toType([1,2,3]); //"array" (all browsers)
// or to test...
var shouldBeAnArray = [1,2,3];
if(Object.toType(shouldBeAnArray) === 'array'){/* do stuff */};
If the only two kinds of values that could be passed to this function are a string or an array of strings, keep it simple and use a typeof check for the string possibility:
function someFunc(arg) {
var arr = (typeof arg == "string") ? [arg] : arg;
}
Here's my lazy approach:
if (Array.prototype.array_ === undefined) {
Array.prototype.array_ = true;
}
// ...
var test = [],
wat = {};
console.log(test.array_ === true); // true
console.log(wat.array_ === true); // false
I know it's sacrilege to "mess with" the prototype, but it appears to perform significantly better than the recommended toString method.
Note: A pitfall of this approach is that it wont work across iframe boundaries, but for my use case this is not an issue.
This function will turn almost anything into an array:
function arr(x) {
if(x === null || x === undefined) {
return [];
}
if(Array.isArray(x)) {
return x;
}
if(isString(x) || isNumber(x)) {
return [x];
}
if(x[Symbol.iterator] !== undefined || x.length !== undefined) {
return Array.from(x);
}
return [x];
}
function isString(x) {
return Object.prototype.toString.call(x) === "[object String]"
}
function isNumber(x) {
return Object.prototype.toString.call(x) === "[object Number]"
}
It uses some newer browser features so you may want to polyfill this for maximum support.
Examples:
> arr(null);
[]
> arr(undefined)
[]
> arr(3.14)
[ 3.14 ]
> arr(1/0)
[ Infinity ]
> gen = function*() { yield 1; yield 2; yield 3; }
[Function: gen]
> arr(gen())
[ 1, 2, 3 ]
> arr([4,5,6])
[ 4, 5, 6 ]
> arr("foo")
[ 'foo' ]
N.B. strings will be converted into an array with a single element instead of an array of chars. Delete the isString check if you would prefer it the other way around.
I've used Array.isArray here because it's the most robust and also simplest.
The following could be used if you know that your object doesn't have a concat method.
var arr = [];
if (typeof arr.concat === 'function') {
console.log("It's an array");
}
var a = [], b = {};
console.log(a.constructor.name == "Array");
console.log(b.constructor.name == "Object");
There is a nice example in Stoyan Stefanov's book JavaScript Patterns which is supposed to handle all possible problems as well as use the ECMAScript 5 method Array.isArray().
So here it is:
if (typeof Array.isArray === "undefined") {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) === "[object Array]";
};
}
By the way, if you are using jQuery, you can use its method $.isArray().
You could use the isArray method, but I would prefer to check with:
Object.getPrototypeOf(yourvariable) === Array.prototype
function isArray(value) {
if (value) {
if (typeof value === 'object') {
return (Object.prototype.toString.call(value) == '[object Array]')
}
}
return false;
}
var ar = ["ff","tt"]
alert(isArray(ar))
A simple function for testing if an input value is an array is the following:
function isArray(value)
{
return Object.prototype.toString.call(value) === '[object Array]';
}
This works cross browser, and with older browsers. This is pulled from T.J. Crowders' blog post
You can try this:
var arr = []; (or) arr = new Array();
var obj = {}; (or) arr = new Object();
arr.constructor.prototype.hasOwnProperty('push') //true
obj.constructor.prototype.hasOwnProperty('push') // false
In your case you may use concat method of Array which can accept single objects as well as array (and even combined):
function myFunc(stringOrArray)
{
var arr = [].concat(stringOrArray);
console.log(arr);
arr.forEach(function(item, i)
{
console.log(i, "=", item);
})
}
myFunc("one string");
myFunc(["one string", "second", "third"]);
concat seems to be one of the oldest methods of Array (even IE 5.5 knows it well).

What is the best way to compare a string (a) to multiple strings [a,b,c...] in JavaScript? (Without JQuery) [duplicate]

Can someone tell me how to detect if "specialword" appears in an array? Example:
categories: [
"specialword"
"word1"
"word2"
]
You really don't need jQuery for this.
var myarr = ["I", "like", "turtles"];
var arraycontainsturtles = (myarr.indexOf("turtles") > -1);
Hint: indexOf returns a number, representing the position where the specified searchvalue occurs for the first time, or -1 if it never
occurs
or
function arrayContains(needle, arrhaystack)
{
return (arrhaystack.indexOf(needle) > -1);
}
It's worth noting that array.indexOf(..) is not supported in IE < 9, but jQuery's indexOf(...) function will work even for those older versions.
jQuery offers $.inArray:
Note that inArray returns the index of the element found, so 0 indicates the element is the first in the array. -1 indicates the element was not found.
var categoriesPresent = ['word', 'word', 'specialword', 'word'];
var categoriesNotPresent = ['word', 'word', 'word'];
var foundPresent = $.inArray('specialword', categoriesPresent) > -1;
var foundNotPresent = $.inArray('specialword', categoriesNotPresent) > -1;
console.log(foundPresent, foundNotPresent); // true false
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Edit 3.5 years later
$.inArray is effectively a wrapper for Array.prototype.indexOf in browsers that support it (almost all of them these days), while providing a shim in those that don't. It is essentially equivalent to adding a shim to Array.prototype, which is a more idiomatic/JSish way of doing things. MDN provides such code. These days I would take this option, rather than using the jQuery wrapper.
var categoriesPresent = ['word', 'word', 'specialword', 'word'];
var categoriesNotPresent = ['word', 'word', 'word'];
var foundPresent = categoriesPresent.indexOf('specialword') > -1;
var foundNotPresent = categoriesNotPresent.indexOf('specialword') > -1;
console.log(foundPresent, foundNotPresent); // true false
Edit another 3 years later
Gosh, 6.5 years?!
The best option for this in modern Javascript is Array.prototype.includes:
var found = categories.includes('specialword');
No comparisons and no confusing -1 results. It does what we want: it returns true or false. For older browsers it's polyfillable using the code at MDN.
var categoriesPresent = ['word', 'word', 'specialword', 'word'];
var categoriesNotPresent = ['word', 'word', 'word'];
var foundPresent = categoriesPresent.includes('specialword');
var foundNotPresent = categoriesNotPresent.includes('specialword');
console.log(foundPresent, foundNotPresent); // true false
Here you go:
$.inArray('specialword', arr)
This function returns a positive integer (the array index of the given value), or -1 if the given value was not found in the array.
Live demo: http://jsfiddle.net/simevidas/5Gdfc/
You probably want to use this like so:
if ( $.inArray('specialword', arr) > -1 ) {
// the value is in the array
}
we can use includes option (which is js built-in function), which will return true if the value is found else it will be false.
if you want the exact index you can use indexOf (which is also js built-in function), which will return the exact index if the value is found else it will return -1.
You can switch .includes with the .some method which returns a boolean.
It will exit as soon as a match was found, which is great for performance for huge arrays:
Note: all are case sensitive
var myarr = ["I", "like", "turtles"];
isVal = myarr.includes('like')
index = myarr.indexOf('like')
some = myarr.some(item => item.toLowerCase() == 'like'.toLowerCase())
console.log(isVal)
console.log(index)
console.log(some)
please check this.
You can use a for loop:
var found = false;
for (var i = 0; i < categories.length && !found; i++) {
if (categories[i] === "specialword") {
found = true;
break;
}
}
With modern javascript's Array methods:
Array.prototype.includes() // introduced in ES7:
returns boolean
const data = {
categories: [
"specialword",
"word1",
"word2"
]
}
console.log("Array.prototype.includes()")
// Array.prototype.includes()
// returns boolean
console.log(data.categories.includes("specialword"))
console.log(data.categories.includes("non-exist"))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array.prototype.find() // introduced in ES6:
returns found element or undefined
const data = {
categories: [
"specialword",
"word1",
"word2"
]
}
console.log("Array.prototype.find()")
// Array.prototype.find()
// returns the element if found
// returns undefined if not found
console.log(data.categories.find(el => el === "specialword") != undefined)
console.log(data.categories.find(el => el === "non-exist") != undefined)
.as-console-wrapper { max-height: 100% !important; top: 0; }
I don't like $.inArray(..), it's the kind of ugly, jQuery-ish solution that most sane people wouldn't tolerate. Here's a snippet which adds a simple contains(str) method to your arsenal:
$.fn.contains = function (target) {
var result = null;
$(this).each(function (index, item) {
if (item === target) {
result = item;
}
});
return result ? result : false;
}
Similarly, you could wrap $.inArray in an extension:
$.fn.contains = function (target) {
return ($.inArray(target, this) > -1);
}

My == isn't working [duplicate]

This question already has answers here:
How to compare arrays in JavaScript?
(55 answers)
Closed 6 years ago.
var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
alert(a == b + "|" + b == c);
demo
How can I check these array for equality and get a method which returns true if they are equal?
Does jQuery offer any method for this?
This is what you should do. Please do not use stringify nor < >.
function arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
[2021 changelog: bugfix for option4: no total ordering on js objects (even excluding NaN!=NaN and '5'==5 ('5'===5, '2'<3, etc.)), so cannot use .sort(cmpFunc) on Map.keys() (though you can on Object.keys(obj), since even 'numerical' keys are strings).]
Option 1
Easiest option, works in almost all cases, except that null!==undefined but they both are converted to JSON representation null and considered equal:
function arraysEqual(a1,a2) {
/* WARNING: arrays must not contain {objects} or behavior may be undefined */
return JSON.stringify(a1)==JSON.stringify(a2);
}
(This might not work if your array contains objects. Whether this still works with objects depends on whether the JSON implementation sorts keys. For example, the JSON of {1:2,3:4} may or may not be equal to {3:4,1:2}; this depends on the implementation, and the spec makes no guarantee whatsoever. [2017 update: Actually the ES6 specification now guarantees object keys will be iterated in order of 1) integer properties, 2) properties in the order they were defined, then 3) symbol properties in the order they were defined. Thus IF the JSON.stringify implementation follows this, equal objects (in the === sense but NOT NECESSARILY in the == sense) will stringify to equal values. More research needed. So I guess you could make an evil clone of an object with properties in the reverse order, but I cannot imagine it ever happening by accident...] At least on Chrome, the JSON.stringify function tends to return keys in the order they were defined (at least that I've noticed), but this behavior is very much subject to change at any point and should not be relied upon. If you choose not to use objects in your lists, this should work fine. If you do have objects in your list that all have a unique id, you can do a1.map(function(x)}{return {id:x.uniqueId}}). If you have arbitrary objects in your list, you can read on for option #2.)
This works for nested arrays as well.
It is, however, slightly inefficient because of the overhead of creating these strings and garbage-collecting them.
Option 2
Historical, version 1 solution:
// generally useful functions
function type(x) { // does not work in general, but works on JSONable objects we care about... modify as you see fit
// e.g. type(/asdf/g) --> "[object RegExp]"
return Object.prototype.toString.call(x);
}
function zip(arrays) {
// e.g. zip([[1,2,3],[4,5,6]]) --> [[1,4],[2,5],[3,6]]
return arrays[0].map(function(_,i){
return arrays.map(function(array){return array[i]})
});
}
// helper functions
function allCompareEqual(array) {
// e.g. allCompareEqual([2,2,2,2]) --> true
// does not work with nested arrays or objects
return array.every(function(x){return x==array[0]});
}
function isArray(x){ return type(x)==type([]) }
function getLength(x){ return x.length }
function allTrue(array){ return array.reduce(function(a,b){return a&&b},true) }
// e.g. allTrue([true,true,true,true]) --> true
// or just array.every(function(x){return x});
function allDeepEqual(things) {
// works with nested arrays
if( things.every(isArray) )
return allCompareEqual(things.map(getLength)) // all arrays of same length
&& allTrue(zip(things).map(allDeepEqual)); // elements recursively equal
//else if( this.every(isObject) )
// return {all have exactly same keys, and for
// each key k, allDeepEqual([o1[k],o2[k],...])}
// e.g. ... && allTrue(objectZip(objects).map(allDeepEqual))
//else if( ... )
// extend some more
else
return allCompareEqual(things);
}
// Demo:
allDeepEqual([ [], [], [] ])
true
allDeepEqual([ [1], [1], [1] ])
true
allDeepEqual([ [1,2], [1,2] ])
true
allDeepEqual([ [[1,2],[3]], [[1,2],[3]] ])
true
allDeepEqual([ [1,2,3], [1,2,3,4] ])
false
allDeepEqual([ [[1,2],[3]], [[1,2],[],3] ])
false
allDeepEqual([ [[1,2],[3]], [[1],[2,3]] ])
false
allDeepEqual([ [[1,2],3], [1,[2,3]] ])
false
<!--
More "proper" option, which you can override to deal with special cases (like regular objects and null/undefined and custom objects, if you so desire):
To use this like a regular function, do:
function allDeepEqual2() {
return allDeepEqual([].slice.call(arguments));
}
Demo:
allDeepEqual2([[1,2],3], [[1,2],3])
true
-->
Option 3
function arraysEqual(a,b) {
/*
Array-aware equality checker:
Returns whether arguments a and b are == to each other;
however if they are equal-lengthed arrays, returns whether their
elements are pairwise == to each other recursively under this
definition.
*/
if (a instanceof Array && b instanceof Array) {
if (a.length!=b.length) // assert same length
return false;
for(var i=0; i<a.length; i++) // assert each element equal
if (!arraysEqual(a[i],b[i]))
return false;
return true;
} else {
return a==b; // if not both arrays, should be the same
}
}
//Examples:
arraysEqual([[1,2],3], [[1,2],3])
true
arraysEqual([1,2,3], [1,2,3,4])
false
arraysEqual([[1,2],[3]], [[1,2],[],3])
false
arraysEqual([[1,2],[3]], [[1],[2,3]])
false
arraysEqual([[1,2],3], undefined)
false
arraysEqual(undefined, undefined)
true
arraysEqual(1, 2)
false
arraysEqual(null, null)
true
arraysEqual(1, 1)
true
arraysEqual([], 1)
false
arraysEqual([], undefined)
false
arraysEqual([], [])
true
/*
If you wanted to apply this to JSON-like data structures with js Objects, you could do so. Fortunately we're guaranteed that all objects keys are unique, so iterate over the objects OwnProperties and sort them by key, then assert that both the sorted key-array is equal and the value-array are equal, and just recurse. We CANNOT extend the sort-then-compare method with Maps as well; even though Map keys are unique, there is no total ordering in ecmascript, so you can't sort them... but you CAN query them individually (see the next section Option 4). (Also if we extend this to Sets, we run into the tree isomorphism problem http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf - fortunately it's not as hard as general graph isomorphism; there is in fact an O(#vertices) algorithm to solve it, but it can get very complicated to do it efficiently. The pathological case is if you have a set made up of lots of seemingly-indistinguishable objects, but upon further inspection some of those objects may differ as you delve deeper into them. You can also work around this by using hashing to reject almost all cases.)
*/
<!--
**edit**: It's 2016 and my previous overcomplicated answer was bugging me. This recursive, imperative "recursive programming 101" implementation keeps the code really simple, and furthermore fails at the earliest possible point (giving us efficiency). It also doesn't generate superfluous ephemeral datastructures (not that there's anything wrong with functional programming in general, but just keeping it clean here).
If we wanted to apply this to a non-empty arrays of arrays, we could do seriesOfArrays.reduce(arraysEqual).
This is its own function, as opposed to using Object.defineProperties to attach to Array.prototype, since that would fail with a key error if we passed in an undefined value (that is however a fine design decision if you want to do so).
This only answers OPs original question.
-->
Option 4:
(continuation of 2016 edit)
This should work with most objects:
const STRICT_EQUALITY_BROKEN = (a,b)=> a===b;
const STRICT_EQUALITY_NO_NAN = (a,b)=> {
if (typeof a=='number' && typeof b=='number' && ''+a=='NaN' && ''+b=='NaN')
// isNaN does not do what you think; see +/-Infinity
return true;
else
return a===b;
};
function deepEquals(a,b, areEqual=STRICT_EQUALITY_NO_NAN, setElementsAreEqual=STRICT_EQUALITY_NO_NAN) {
/* compares objects hierarchically using the provided
notion of equality (defaulting to ===);
supports Arrays, Objects, Maps, ArrayBuffers */
if (a instanceof Array && b instanceof Array)
return arraysEqual(a,b, areEqual);
if (Object.getPrototypeOf(a)===Object.prototype && Object.getPrototypeOf(b)===Object.prototype)
return objectsEqual(a,b, areEqual);
if (a instanceof Map && b instanceof Map)
return mapsEqual(a,b, areEqual);
if (a instanceof Set && b instanceof Set) {
if (setElementsAreEqual===STRICT_EQUALITY_NO_NAN)
return setsEqual(a,b);
else
throw "Error: set equality by hashing not implemented because cannot guarantee custom notion of equality is transitive without programmer intervention."
}
if ((a instanceof ArrayBuffer || ArrayBuffer.isView(a)) && (b instanceof ArrayBuffer || ArrayBuffer.isView(b)))
return typedArraysEqual(a,b);
return areEqual(a,b); // see note[1] -- IMPORTANT
}
function arraysEqual(a,b, areEqual) {
if (a.length!=b.length)
return false;
for(var i=0; i<a.length; i++)
if (!deepEquals(a[i],b[i], areEqual))
return false;
return true;
}
function objectsEqual(a,b, areEqual) {
var aKeys = Object.getOwnPropertyNames(a);
var bKeys = Object.getOwnPropertyNames(b);
if (aKeys.length!=bKeys.length)
return false;
aKeys.sort();
bKeys.sort();
for(var i=0; i<aKeys.length; i++)
if (!areEqual(aKeys[i],bKeys[i])) // keys must be strings
return false;
return deepEquals(aKeys.map(k=>a[k]), aKeys.map(k=>b[k]), areEqual);
}
function mapsEqual(a,b, areEqual) { // assumes Map's keys use the '===' notion of equality, which is also the assumption of .has and .get methods in the spec; however, Map's values use our notion of the areEqual parameter
if (a.size!=b.size)
return false;
return [...a.keys()].every(k=>
b.has(k) && deepEquals(a.get(k), b.get(k), areEqual)
);
}
function setsEqual(a,b) {
// see discussion in below rest of StackOverflow answer
return a.size==b.size && [...a.keys()].every(k=>
b.has(k)
);
}
function typedArraysEqual(a,b) {
// we use the obvious notion of equality for binary data
a = new Uint8Array(a);
b = new Uint8Array(b);
if (a.length != b.length)
return false;
for(var i=0; i<a.length; i++)
if (a[i]!=b[i])
return false;
return true;
}
Demo (not extensively tested):
var nineTen = new Float32Array(2);
nineTen[0]=9; nineTen[1]=10;
> deepEquals(
[[1,[2,3]], 4, {a:5,'111':6}, new Map([['c',7],['d',8]]), nineTen],
[[1,[2,3]], 4, {111:6,a:5}, new Map([['d',8],['c',7]]), nineTen]
)
true
> deepEquals(
[[1,[2,3]], 4, {a:'5','111':6}, new Map([['c',7],['d',8]]), nineTen],
[[1,[2,3]], 4, {111:6,a:5}, new Map([['d',8],['c',7]]), nineTen],
(a,b)=>a==b
)
true
Note that if one is using the == notion of equality, then know that falsey values and coercion means that == equality is NOT TRANSITIVE. For example ''==0 and 0=='0' but ''!='0'. This is relevant for Sets: I do not think one can override the notion of Set equality in a meaningful way. If one is using the built-in notion of Set equality (that is, ===), then the above should work. However if one uses a non-transitive notion of equality like ==, you open a can of worms: Even if you forced the user to define a hash function on the domain (hash(a)!=hash(b) implies a!=b) I'm not sure that would help... Certainly one could do the O(N^2) performance thing and remove pairs of == items one by one like a bubble sort, and then do a second O(N^2) pass to confirm things in equivalence classes are actually == to each other, and also != to everything not thus paired, but you'd STILL have to throw a runtime error if you have some coercion going on... You'd also maybe get weird (but potentially not that weird) edge cases with https://developer.mozilla.org/en-US/docs/Glossary/Falsy and Truthy values (with the exception that NaN==NaN... but just for Sets!). This is not an issue usually with most Sets of homogenous datatype.
To summarize the complexity of recursive equality on Sets:
Set equality is the tree isomorphism problem http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf but a bit simpler
set A =? set B being synonymous with B.has(k) for every k in A implicitly uses ===-equality ([1,2,3]!==[1,2,3]), not recursive equality (deepEquals([1,2,3],[1,2,3]) == true), so two new Set([[1,2,3]]) would not be equal because we don't recurse
trying to get recursive equality to work is kind of meaningless if the recursive notion of equality you use is not 1) reflexive (a=b implies b=a) and 2) symmetric (a=a) and 3) transitive (a=b and b=c implies a=c); this is the definition of an equivalence class
the equality == operator obviously does not obey many of these properties
even the strict equality === operator in ecmascript
does not obey these properties, because the strict equality comparison algorithm of ecmascript has NaN!=NaN; this is why many native datatypes like Set and Map 'equate' NaNs to consider them the same values when they appear as keys
As long as we force and ensure recursive set equality is indeed transitive and reflexive and symmetric, we can make sure nothing horribly wrong happens.
Then, we can do O(N^2) comparisons by recursively comparing everything randomly, which is incredibly inefficient. There is no magical algorithm that lets us do setKeys.sort((a,b)=> /*some comparison function*/) because there is no total ordering in ecmascript (''==0 and 0=='0', but ''!='0'... though I believe you might be able to define one yourself which would certainly be a lofty goal).
We can however .toStringify or JSON.stringify all elements to assist us. We will then sort them, which gives us equivalence classes (two same things won't not have the same string JSON representation) of potentially-false-positives (two different things may have the same string or JSON representation).
However, this introduces its own performance issues because serializing the same thing, then serializing subsets of that thing, over and over, is incredibly inefficient. Imagine a tree of nested Sets; every node would belong to O(depth) different serializations!
Even if that was not an issue, the worst-case performance would still be O(N!) if all the serializations 'hints' were the same
Thus, the above implementation declares that Sets are equal if the items are just plain === (not recursively ===). This will mean that it will return false for new Set([1,2,3]) and new Set([1,2,3]). With a bit of effort, you may rewrite that part of the code if you know what you're doing.
(sidenote: Maps are es6 dictionaries. I can't tell if they have O(1) or O(log(N)) lookup performance, but in any case they are 'ordered' in the sense that they keep track of the order in which key-value pairs were inserted into them. However, the semantic of whether two Maps should be equal if elements were inserted in a different order into them is ambiguous. I give a sample implementation below of a deepEquals that considers two maps equal even if elements were inserted into them in a different order.)
(note [1]: IMPORTANT: NOTION OF EQUALITY: You may want to override the noted line with a custom notion of equality, which you'll also have to change in the other functions anywhere it appears. For example, do you or don't you want NaN==NaN? By default this is not the case. There are even more weird things like 0=='0'. Do you consider two objects to be the same if and only if they are the same object in memory? See https://stackoverflow.com/a/5447170/711085 . You should document the notion of equality you use.) Also note that other answers which naively use .toString and .sort may sometimes fall pray to the fact that 0!=-0 but are considered equal and canonicalizable to 0 for almost all datatypes and JSON serialization; whether -0==0 should also be documented in your notion of equality, as well as most other things in that table like NaN, etc.
You should be able to extend the above to WeakMaps, WeakSets. Not sure if it makes sense to extend to DataViews. Should also be able to extend to RegExps probably, etc.
As you extend it, you realize you do lots of unnecessary comparisons. This is where the type function that I defined way earlier (solution #2) can come in handy; then you can dispatch instantly. Whether that is worth the overhead of (possibly? not sure how it works under the hood) string representing the type is up to you. You can just then rewrite the dispatcher, i.e. the function deepEquals, to be something like:
var dispatchTypeEquals = {
number: function(a,b) {...a==b...},
array: function(a,b) {...deepEquals(x,y)...},
...
}
function deepEquals(a,b) {
var typeA = extractType(a);
var typeB = extractType(a);
return typeA==typeB && dispatchTypeEquals[typeA](a,b);
}
jQuery does not have a method for comparing arrays. However the Underscore library (or the comparable Lodash library) does have such a method: isEqual, and it can handle a variety of other cases (like object literals) as well. To stick to the provided example:
var a=[1,2,3];
var b=[3,2,1];
var c=new Array(1,2,3);
alert(_.isEqual(a, b) + "|" + _.isEqual(b, c));
By the way: Underscore has lots of other methods that jQuery is missing as well, so it's a great complement to jQuery.
EDIT: As has been pointed out in the comments, the above now only works if both arrays have their elements in the same order, ie.:
_.isEqual([1,2,3], [1,2,3]); // true
_.isEqual([1,2,3], [3,2,1]); // false
Fortunately Javascript has a built in method for for solving this exact problem, sort:
_.isEqual([1,2,3].sort(), [3,2,1].sort()); // true
For primitive values like numbers and strings this is an easy solution:
a = [1,2,3]
b = [3,2,1]
a.sort().toString() == b.sort().toString()
The call to sort() will ensure that the order of the elements does not matter. The toString() call will create a string with the values comma separated so both strings can be tested for equality.
With JavaScript version 1.6 it's as easy as this:
Array.prototype.equals = function( array ) {
return this.length == array.length &&
this.every( function(this_i,i) { return this_i == array[i] } )
}
For example, [].equals([]) gives true, while [1,2,3].equals( [1,3,2] ) yields false.
Even if this would seem super simple, sometimes it's really useful. If all you need is to see if two arrays have the same items and they are in the same order, try this:
[1, 2, 3].toString() == [1, 2, 3].toString()
true
[1, 2, 3,].toString() == [1, 2, 3].toString()
true
[1,2,3].toString() == [1, 2, 3].toString()
true
However, this doesn't work for mode advanced cases such as:
[[1,2],[3]].toString() == [[1],[2,3]].toString()
true
It depends what you need.
Based on Tim James answer and Fox32's comment, the following should check for nulls, with the assumption that two nulls are not equal.
function arrays_equal(a,b) { return !!a && !!b && !(a<b || b<a); }
> arrays_equal([1,2,3], [1,3,4])
false
> arrays_equal([1,2,3], [1,2,3])
true
> arrays_equal([1,3,4], [1,2,3])
false
> arrays_equal(null, [1,2,3])
false
> arrays_equal(null, null)
false
jQuery has such method for deep recursive comparison.
A homegrown general purpose strict equality check could look as follows:
function deepEquals(obj1, obj2, parents1, parents2) {
"use strict";
var i;
// compare null and undefined
if (obj1 === undefined || obj2 === undefined ||
obj1 === null || obj2 === null) {
return obj1 === obj2;
}
// compare primitives
if (typeof (obj1) !== 'object' || typeof (obj2) !== 'object') {
return obj1.valueOf() === obj2.valueOf();
}
// if objects are of different types or lengths they can't be equal
if (obj1.constructor !== obj2.constructor || (obj1.length !== undefined && obj1.length !== obj2.length)) {
return false;
}
// iterate the objects
for (i in obj1) {
// build the parents list for object on the left (obj1)
if (parents1 === undefined) parents1 = [];
if (obj1.constructor === Object) parents1.push(obj1);
// build the parents list for object on the right (obj2)
if (parents2 === undefined) parents2 = [];
if (obj2.constructor === Object) parents2.push(obj2);
// walk through object properties
if (obj1.propertyIsEnumerable(i)) {
if (obj2.propertyIsEnumerable(i)) {
// if object at i was met while going down here
// it's a self reference
if ((obj1[i].constructor === Object && parents1.indexOf(obj1[i]) >= 0) || (obj2[i].constructor === Object && parents2.indexOf(obj2[i]) >= 0)) {
if (obj1[i] !== obj2[i]) {
return false;
}
continue;
}
// it's not a self reference so we are here
if (!deepEquals(obj1[i], obj2[i], parents1, parents2)) {
return false;
}
} else {
// obj2[i] does not exist
return false;
}
}
}
return true;
};
Tests:
// message is displayed on failure
// clean console === all tests passed
function assertTrue(cond, msg) {
if (!cond) {
console.log(msg);
}
}
var a = 'sdf',
b = 'sdf';
assertTrue(deepEquals(b, a), 'Strings are equal.');
b = 'dfs';
assertTrue(!deepEquals(b, a), 'Strings are not equal.');
a = 9;
b = 9;
assertTrue(deepEquals(b, a), 'Numbers are equal.');
b = 3;
assertTrue(!deepEquals(b, a), 'Numbers are not equal.');
a = false;
b = false;
assertTrue(deepEquals(b, a), 'Booleans are equal.');
b = true;
assertTrue(!deepEquals(b, a), 'Booleans are not equal.');
a = null;
assertTrue(!deepEquals(b, a), 'Boolean is not equal to null.');
a = function () {
return true;
};
assertTrue(deepEquals(
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
],
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
]), 'Arrays are equal.');
assertTrue(!deepEquals(
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
],
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': '1'
},
true]
]), 'Arrays are not equal.');
a = {
prop: 'val'
};
a.self = a;
b = {
prop: 'val'
};
b.self = a;
assertTrue(deepEquals(b, a), 'Immediate self referencing objects are equal.');
a.prop = 'shmal';
assertTrue(!deepEquals(b, a), 'Immediate self referencing objects are not equal.');
a = {
prop: 'val',
inside: {}
};
a.inside.self = a;
b = {
prop: 'val',
inside: {}
};
b.inside.self = a;
assertTrue(deepEquals(b, a), 'Deep self referencing objects are equal.');
b.inside.self = b;
assertTrue(!deepEquals(b, a), 'Deep self referencing objects are not equeal. Not the same instance.');
b.inside.self = {foo: 'bar'};
assertTrue(!deepEquals(b, a), 'Deep self referencing objects are not equal. Completely different object.');
a = {};
b = {};
a.self = a;
b.self = {};
assertTrue(!deepEquals(b, a), 'Empty object and self reference of an empty object.');
If you are using lodash and don't want to modify either array, you can use the function _.xor(). It compares the two arrays as sets and returns the set that contains their difference. If the length of this difference is zero, the two arrays are essentially equal:
var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
_.xor(a, b).length === 0
true
_.xor(b, c).length === 0
true
Check every each value by a for loop once you checked the size of the array.
function equalArray(a, b) {
if (a.length === b.length) {
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
} else {
return false;
}
}
Using map() and reduce():
function arraysEqual (a1, a2) {
return a1 === a2 || (
a1 !== null && a2 !== null &&
a1.length === a2.length &&
a1
.map(function (val, idx) { return val === a2[idx]; })
.reduce(function (prev, cur) { return prev && cur; }, true)
);
}
If you wish to check arrays of objects for equality and order does NOT matter, i.e.
areEqual([{id: "0"}, {id: "1"}], [{id: "1"}, {id: "0"}]) // true
you'll want to sort the arrays first. lodash has all the tools you'll need, by combining sortBy and isEqual:
// arr1 & arr2: Arrays of objects
// sortProperty: the property of the object with which you want to sort
// Note: ensure every object in both arrays has your chosen sortProperty
// For example, arr1 = [{id: "v-test_id0"}, {id: "v-test_id1"}]
// and arr2 = [{id: "v-test_id1"}, {id: "v-test_id0"}]
// sortProperty should be 'id'
function areEqual (arr1, arr2, sortProperty) {
return _.areEqual(_.sortBy(arr1, sortProperty), _.sortBy(arr2, sortProperty))
}
EDIT: Since sortBy returns a new array, there is no need to clone your arrays before sorting. The original arrays will not be mutated.
Note that for lodash's isEqual, order does matter. The above example will return false if sortBy is not applied to each array first.
This method sucks, but I've left it here for reference so others avoid this path:
Using Option 1 from #ninjagecko worked best for me:
Array.prototype.equals = function(array) {
return array instanceof Array && JSON.stringify(this) === JSON.stringify(array) ;
}
a = [1, [2, 3]]
a.equals([[1, 2], 3]) // false
a.equals([1, [2, 3]]) // true
It will also handle the null and undefined case, since we're adding this to the prototype of array and checking that the other argument is also an array.
There is no easy way to do this. I needed this as well, but wanted a function that can take any two variables and test for equality. That includes non-object values, objects, arrays and any level of nesting.
In your question, you mention wanting to ignore the order of the values in an array. My solution doesn't inherently do that, but you can achieve it by sorting the arrays before comparing for equality
I also wanted the option of casting non-objects to strings so that [1,2]===["1",2]
Since my project uses UnderscoreJs, I decided to make it a mixin rather than a standalone function.
You can test it out on http://jsfiddle.net/nemesarial/T44W4/
Here is my mxin:
_.mixin({
/**
Tests for the equality of two variables
valA: first variable
valB: second variable
stringifyStatics: cast non-objects to string so that "1"===1
**/
equal:function(valA,valB,stringifyStatics){
stringifyStatics=!!stringifyStatics;
//check for same type
if(typeof(valA)!==typeof(valB)){
if((_.isObject(valA) || _.isObject(valB))){
return false;
}
}
//test non-objects for equality
if(!_.isObject(valA)){
if(stringifyStatics){
var valAs=''+valA;
var valBs=''+valB;
ret=(''+valA)===(''+valB);
}else{
ret=valA===valB;
}
return ret;
}
//test for length
if(_.size(valA)!=_.size(valB)){
return false;
}
//test for arrays first
var isArr=_.isArray(valA);
//test whether both are array or both object
if(isArr!==_.isArray(valB)){
return false;
}
var ret=true;
if(isArr){
//do test for arrays
_.each(valA,function(val,idx,lst){
if(!ret){return;}
ret=ret && _.equal(val,valB[idx],stringifyStatics);
});
}else{
//do test for objects
_.each(valA,function(val,idx,lst){
if(!ret){return;}
//test for object member exists
if(!_.has(valB,idx)){
ret=false;
return;
}
// test for member equality
ret=ret && _.equal(val,valB[idx],stringifyStatics);
});
}
return ret;
}
});
This is how you use it:
_.equal([1,2,3],[1,2,"3"],true)
To demonstrate nesting, you can do this:
_.equal(
['a',{b:'b',c:[{'someId':1},2]},[1,2,3]],
['a',{b:'b',c:[{'someId':"1"},2]},["1",'2',3]]
,true);
It handle all possible stuff and even reference itself in structure of object. You can see the example at the end of code.
var deepCompare = (function() {
function internalDeepCompare (obj1, obj2, objects) {
var i, objPair;
if (obj1 === obj2) {
return true;
}
i = objects.length;
while (i--) {
objPair = objects[i];
if ( (objPair.obj1 === obj1 && objPair.obj2 === obj2) ||
(objPair.obj1 === obj2 && objPair.obj2 === obj1) ) {
return true;
}
}
objects.push({obj1: obj1, obj2: obj2});
if (obj1 instanceof Array) {
if (!(obj2 instanceof Array)) {
return false;
}
i = obj1.length;
if (i !== obj2.length) {
return false;
}
while (i--) {
if (!internalDeepCompare(obj1[i], obj2[i], objects)) {
return false;
}
}
}
else {
switch (typeof obj1) {
case "object":
// deal with null
if (!(obj2 && obj1.constructor === obj2.constructor)) {
return false;
}
if (obj1 instanceof RegExp) {
if (!(obj2 instanceof RegExp && obj1.source === obj2.source)) {
return false;
}
}
else if (obj1 instanceof Date) {
if (!(obj2 instanceof Date && obj1.getTime() === obj2.getTime())) {
return false;
}
}
else {
for (i in obj1) {
if (obj1.hasOwnProperty(i)) {
if (!(obj2.hasOwnProperty(i) && internalDeepCompare(obj1[i], obj2[i], objects))) {
return false;
}
}
}
}
break;
case "function":
if (!(typeof obj2 === "function" && obj1+"" === obj2+"")) {
return false;
}
break;
default: //deal with NaN
if (obj1 !== obj2 && obj1 === obj1 && obj2 === obj2) {
return false;
}
}
}
return true;
}
return function (obj1, obj2) {
return internalDeepCompare(obj1, obj2, []);
};
}());
/*
var a = [a, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null],
b = [b, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null];
deepCompare(a, b);
*/
var a= [1, 2, 3, '3'];
var b = [1, 2, 3];
var c = a.filter(function (i) { return ! ~b.indexOf(i); });
alert(c.length);

is this valid JavaScript arrays comparing algorithm?

Primordial topic, I know, but can anyone please comment on the following algorithm for comparing two ES5 arrrays:
function equal_arrays(a, b) {
"use strict";
var c = 0;
return a.every(function (e) {
return e === b[c++];
});
}
I think this is very clean and obvious use of [].every(), to quickly compare two arrays?
I have that nagging "surely it can not be that simple ?" feeling about it ?
NOTE: two arrays are equal when they contain all elements in the same position strictly equal to each other. So both the element index and element value have to be exactly equal. Values of different types are considered not equal. Sparse arrays are also compared.
TEST CASES:
equal_arrays([],[]) ; // => true
equal_arrays([1,2],[1,2]) ; // => true
equal_arrays([1,,2],[1,,2]) ; // => true
equal_arrays([,],[,]); // => true
equal_arrays([1,,3,,,],[1,,3,,,]); // => true
Uses cases yielding => false, one can imagine herself. Comparing non-arrays is a syntax error.
Many thanks to the well meaning and helpful contributors. It appears that the "best" (there is never such a thing) implementation is this:
function has(element, index)
{
return this[index] === element;
}
function equal_arrays(a, b)
{
return (a.length === b.length) && a.every(has, b) && b.every(has, a);
}
#tom's implementation of the two-way every() which is necessary sp that test cases like this one work:
equal_arrays([1,,3],[1,2,3]); //=> false
Once again thanks ...
No, it is not valid. From the MDN docs:
callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.
So if the first array has 'gaps' they will be skipped over.
equal_arrays([1, 2, , 4], [1, 2, 4]); // true
equal_arrays([1, 2, 4], [1, 2, , 4]); // false
Here is a better implementation:
function equal_arrays(a, b) {
if (a.length != b.length) return false;
var i;
for (i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
Here is an elegant implementation of #RobG's two-way every() idea:
function has(element, index)
{
return this[index] === element;
}
function equal_arrays(a, b)
{
return (a.length === b.length) && a.every(has, b) && b.every(has, a);
}
I have that nagging "surely it can not be that simple ?" feeling about it ?
No, it isn't. Using every on one array will test it for every member of that array but not the other. You have to test both ways.
Also, it depends on your criteria for "equal". You might want every member of the same index to have the same value, but you might not be concerned about order so is [1,2] equal to [2,1]? and is [,,,2] equal to [2]?
Also, should [1,,2] equal [1,undefined,2], i.e. should a member that doesn't exist be "equal" to one that does but has the value undefined?
An array comparison function
The following may do the job. It compares both ways, checking own properties and values. I think it covers all cases but am willing to be convinced otherwise. Might be better as a separate function, but adding to Array.prototype is convenient.
// Test that own properties of two arrays have the same values
Array.prototype.isEqualTo = function(a) {
var visited = {};
if (this.length != a.length) return false;
// Test every own property of this, remember tested properties
for (var p in this) {
if (this.hasOwnProperty(p)) {
visited[p] = p;
if (!(a.hasOwnProperty(p)) || this[p] !== a[p]) {
return false;
}
}
}
// Reverse test that comparison array only has tested properties
for (var q in a) {
if (a.hasOwnProperty(q) && !(q in this)) {
return false;
}
}
return true;
}
console.log([1,,2].isEqualTo([1,undefined,2])); // false, not defined != undefined
console.log([1,,2].isEqualTo([1,2])); // false, different length
console.log([1,2].isEqualTo([1,2])); // true
Note that inherited properties should be ignored as if arrays from different windows are compared (e.g. one is from a frame) then inherited properties won't be equal.
As an alternative, if you just want to check if both arrays are exactly the same you could just do this:
var a = [1,2,3,4];
var b = [1,2,3,4];
JSON.stringify(a) == JSON.stringify(b); //= true
That should work with arrays of numbers and strings.
As CrazyTrain said, the increment value is useless and you need to check for length first:
function equalArrays( a, b ) {
"use strict";
if (a.length !== b.length) return false;
return a.filter(function(el){ return el;}).every(function(e, i) {
return e === b[i];
});
}
Is a valid algorithm to compare arrays by value.

How to find if an array contains a specific string in JavaScript/jQuery?

Can someone tell me how to detect if "specialword" appears in an array? Example:
categories: [
"specialword"
"word1"
"word2"
]
You really don't need jQuery for this.
var myarr = ["I", "like", "turtles"];
var arraycontainsturtles = (myarr.indexOf("turtles") > -1);
Hint: indexOf returns a number, representing the position where the specified searchvalue occurs for the first time, or -1 if it never
occurs
or
function arrayContains(needle, arrhaystack)
{
return (arrhaystack.indexOf(needle) > -1);
}
It's worth noting that array.indexOf(..) is not supported in IE < 9, but jQuery's indexOf(...) function will work even for those older versions.
jQuery offers $.inArray:
Note that inArray returns the index of the element found, so 0 indicates the element is the first in the array. -1 indicates the element was not found.
var categoriesPresent = ['word', 'word', 'specialword', 'word'];
var categoriesNotPresent = ['word', 'word', 'word'];
var foundPresent = $.inArray('specialword', categoriesPresent) > -1;
var foundNotPresent = $.inArray('specialword', categoriesNotPresent) > -1;
console.log(foundPresent, foundNotPresent); // true false
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Edit 3.5 years later
$.inArray is effectively a wrapper for Array.prototype.indexOf in browsers that support it (almost all of them these days), while providing a shim in those that don't. It is essentially equivalent to adding a shim to Array.prototype, which is a more idiomatic/JSish way of doing things. MDN provides such code. These days I would take this option, rather than using the jQuery wrapper.
var categoriesPresent = ['word', 'word', 'specialword', 'word'];
var categoriesNotPresent = ['word', 'word', 'word'];
var foundPresent = categoriesPresent.indexOf('specialword') > -1;
var foundNotPresent = categoriesNotPresent.indexOf('specialword') > -1;
console.log(foundPresent, foundNotPresent); // true false
Edit another 3 years later
Gosh, 6.5 years?!
The best option for this in modern Javascript is Array.prototype.includes:
var found = categories.includes('specialword');
No comparisons and no confusing -1 results. It does what we want: it returns true or false. For older browsers it's polyfillable using the code at MDN.
var categoriesPresent = ['word', 'word', 'specialword', 'word'];
var categoriesNotPresent = ['word', 'word', 'word'];
var foundPresent = categoriesPresent.includes('specialword');
var foundNotPresent = categoriesNotPresent.includes('specialword');
console.log(foundPresent, foundNotPresent); // true false
Here you go:
$.inArray('specialword', arr)
This function returns a positive integer (the array index of the given value), or -1 if the given value was not found in the array.
Live demo: http://jsfiddle.net/simevidas/5Gdfc/
You probably want to use this like so:
if ( $.inArray('specialword', arr) > -1 ) {
// the value is in the array
}
we can use includes option (which is js built-in function), which will return true if the value is found else it will be false.
if you want the exact index you can use indexOf (which is also js built-in function), which will return the exact index if the value is found else it will return -1.
You can switch .includes with the .some method which returns a boolean.
It will exit as soon as a match was found, which is great for performance for huge arrays:
Note: all are case sensitive
var myarr = ["I", "like", "turtles"];
isVal = myarr.includes('like')
index = myarr.indexOf('like')
some = myarr.some(item => item.toLowerCase() == 'like'.toLowerCase())
console.log(isVal)
console.log(index)
console.log(some)
please check this.
You can use a for loop:
var found = false;
for (var i = 0; i < categories.length && !found; i++) {
if (categories[i] === "specialword") {
found = true;
break;
}
}
With modern javascript's Array methods:
Array.prototype.includes() // introduced in ES7:
returns boolean
const data = {
categories: [
"specialword",
"word1",
"word2"
]
}
console.log("Array.prototype.includes()")
// Array.prototype.includes()
// returns boolean
console.log(data.categories.includes("specialword"))
console.log(data.categories.includes("non-exist"))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array.prototype.find() // introduced in ES6:
returns found element or undefined
const data = {
categories: [
"specialword",
"word1",
"word2"
]
}
console.log("Array.prototype.find()")
// Array.prototype.find()
// returns the element if found
// returns undefined if not found
console.log(data.categories.find(el => el === "specialword") != undefined)
console.log(data.categories.find(el => el === "non-exist") != undefined)
.as-console-wrapper { max-height: 100% !important; top: 0; }
I don't like $.inArray(..), it's the kind of ugly, jQuery-ish solution that most sane people wouldn't tolerate. Here's a snippet which adds a simple contains(str) method to your arsenal:
$.fn.contains = function (target) {
var result = null;
$(this).each(function (index, item) {
if (item === target) {
result = item;
}
});
return result ? result : false;
}
Similarly, you could wrap $.inArray in an extension:
$.fn.contains = function (target) {
return ($.inArray(target, this) > -1);
}

Categories

Resources