Related
I've observed this in Firefox-3.5.7/Firebug-1.5.3 and Firefox-3.6.16/Firebug-1.6.2
When I fire up Firebug:
var x = new Array(3)
console.log(x)
// [undefined, undefined, undefined]
var y = [undefined, undefined, undefined]
console.log(y)
// [undefined, undefined, undefined]
console.log( x.constructor == y.constructor) // true
console.log(
x.map(function() { return 0; })
)
// [undefined, undefined, undefined]
console.log(
y.map(function() { return 0; })
)
// [0, 0, 0]
What's going on here? Is this a bug, or am I misunderstanding how to use new Array(3)?
I had a task that I only knew the length of the array and needed to transform the items.
I wanted to do something like this:
let arr = new Array(10).map((val,idx) => idx);
To quickly create an array like this:
[0,1,2,3,4,5,6,7,8,9]
But it didn't work because:
(see Jonathan Lonowski's answer)
The solution could be to fill up the array items with any value (even with undefined) using Array.prototype.fill()
let arr = new Array(10).fill(undefined).map((val,idx) => idx);
console.log(new Array(10).fill(undefined).map((val, idx) => idx));
Update
Another solution could be:
let arr = Array.apply(null, Array(10)).map((val, idx) => idx);
console.log(Array.apply(null, Array(10)).map((val, idx) => idx));
It appears that the first example
x = new Array(3);
Creates an array with a length of 3 but without any elements, so the indices [0], [1] and [2] is not created.
And the second creates an array with the 3 undefined objects, in this case the indices/properties them self are created but the objects they refer to are undefined.
y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];
As map runs on the list of indices/properties, not on the set length, so if no indices/properties is created, it will not run.
With ES6, you can do [...Array(10)].map((a, b) => a) , quick and easy!
From the MDC page for map:
[...] callback is invoked only for indexes of the array which have assigned value; [...]
[undefined] actually applies the setter on the index(es) so that map will iterate, whereas new Array(1) just initializes the index(es) with a default value of undefined so map skips it.
I believe this is the same for all iteration methods.
ES6 solution:
[...Array(10)]
Doesn't work on typescript (2.3), though
The arrays are different. The difference is that new Array(3) creates an array with a length of three but no properties, while [undefined, undefined, undefined] creates an array with a length of three and three properties called "0", "1" and "2", each with a value of undefined. You can see the difference using the in operator:
"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true
This stems from the slightly confusing fact that if you try to get the value of a non-existent property of any native object in JavaScript, it returns undefined (rather than throwing an error, as happens when you try to refer to a non-existent variable), which is the same as what you get if the property has previously been explictly set to undefined.
For reasons thoroughly explained in other answers, Array(n).map doesn't work. However, in ES2015 Array.from accepts a map function:
let array1 = Array.from(Array(5), (_, i) => i + 1)
console.log('array1', JSON.stringify(array1)) // 1,2,3,4,5
let array2 = Array.from({length: 5}, (_, i) => (i + 1) * 2)
console.log('array2', JSON.stringify(array2)) // 2,4,6,8,10
In ECMAScript 6th edition specification.
new Array(3) only define property length and do not define index properties like {length: 3}. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len Step 9.
[undefined, undefined, undefined] will define index properties and length property like {0: undefined, 1: undefined, 2: undefined, length: 3}. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList Step 5.
methods map, every, some, forEach, slice, reduce, reduceRight, filter of Array will check the index property by HasProperty internal method, so new Array(3).map(v => 1) will not invoke the callback.
for more detail, see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map
How to fix?
let a = new Array(3);
a.join('.').split('.').map(v => 1);
let a = new Array(3);
a.fill(1);
let a = new Array(3);
a.fill(undefined).map(v => 1);
let a = new Array(3);
[...a].map(v => 1);
I think the best way to explain this is to look at the way that Chrome handles it.
>>> x = new Array(3)
[]
>>> x.length
3
So what is actually happening is that new Array() is returning an empty array that has a length of 3, but no values. Therefore, when you run x.map on a technically empty array, there is nothing to be set.
Firefox just 'fills in' those empty slots with undefined even though it has no values.
I don't think this is explicitly a bug, just a poor way of representing what is going on. I suppose Chrome's is "more correct" because it shows that there isn't actually anything in the array.
Not a bug. That's how the Array constructor is defined to work.
From MDC:
When you specify a single numeric parameter with the Array constructor, you specify the initial length of the array. The following code creates an array of five elements:
var billingMethod = new Array(5);
The behavior of the Array constructor depends on whether the single parameter is a number.
The .map() method only includes in the iteration elements of the array that have explicitly had values assigned. Even an explicit assignment of undefined will cause a value to be considered eligible for inclusion in the iteration. That seems odd, but it's essentially the difference between an explicit undefined property on an object and a missing property:
var x = { }, y = { z: undefined };
if (x.z === y.z) // true
The object x does not have a property called "z", and the object y does. However, in both cases it appears that the "value" of the property is undefined. In an array, the situation is similar: the value of length does implicitly perform a value assignment to all the elements from zero through length - 1. The .map() function therefore won't do anything (won't call the callback) when called on an array newly constructed with the Array constructor and a numeric argument.
Just ran into this. It sure would be convenient to be able to use Array(n).map.
Array(3) yields roughly {length: 3}
[undefined, undefined, undefined] creates the numbered properties:
{0: undefined, 1: undefined, 2: undefined, length: 3}.
The map() implementation only acts on defined properties.
If you are doing this in order to easily fill up an array with values, can't use fill for browser support reasons and really don't want to do a for-loop, you can also do x = new Array(3).join(".").split(".").map(... which will give you an array of empty strings.
Quite ugly I have to say, but at least the problem and intention are quite clearly communicated.
Since the question is why, this has to do with how JS was designed.
There are 2 main reasons I can think of to explain this behavior:
Performance: Given x = 10000 and new Array(x) it is wise for the constructor to avoid looping from 0 to 10000 to fill the array with undefined values.
Implicitly "undefined": Give a = [undefined, undefined] and b = new Array(2), a[1] and b[1] will both return undefined, but a[8] and b[8] will also return undefined even if they're out of range.
Ultimately, the notation empty x 3 is a shortcut to avoid setting and displaying a long list of undefined values that are undefined anyway because they are not declared explicitly.
Note: Given array a = [0] and a[9] = 9, console.log(a) will return (10) [0, empty x 8, 9], filling the gap automatically by returning the difference between the two values declared explicitly.
Here's a simple utility method as a workaround:
Simple mapFor
function mapFor(toExclusive, callback) {
callback = callback || function(){};
var arr = [];
for (var i = 0; i < toExclusive; i++) {
arr.push(callback(i));
}
return arr;
};
var arr = mapFor(3, function(i){ return i; });
console.log(arr); // [0, 1, 2]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]
Complete Example
Here's a more complete example (with sanity checks) which also allows specifying an optional starting index:
function mapFor() {
var from, toExclusive, callback;
if (arguments.length == 3) {
from = arguments[0];
toExclusive = arguments[1];
callback = arguments[2];
} else if (arguments.length == 2) {
if (typeof arguments[1] === 'function') {
from = 0;
toExclusive = arguments[0];
callback = arguments[1];
} else {
from = arguments[0];
toExclusive = arguments[1];
}
} else if (arguments.length == 1) {
from = 0;
toExclusive = arguments[0];
}
callback = callback || function () {};
var arr = [];
for (; from < toExclusive; from++) {
arr.push(callback(from));
}
return arr;
}
var arr = mapFor(1, 3, function (i) { return i; });
console.log(arr); // [1, 2]
arr = mapFor(1, 3);
console.log(arr); // [undefined, undefined]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]
Counting Down
Manipulating the index passed to the callback allows counting backwards:
var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
return count - 1 - i;
});
// arr = [2, 1, 0]
When dealing with arrays of coordinates, seen as arrays of length 2, it is necessary to check if some coordinate is contained in that array. However, JavaScript cannot really do that directly (here I use that ES2016 method Array.includes, but with the more classical Array.indexOf the same issue appears):
const a = [[1,2],[5,6]];
const find = a.includes([5,6]);
console.log(find);
This returns false. This has always bothered me. Can someone explain to me why it returns false? To solve this issue, I usually define a helper function:
function hasElement(arr,el) {
return arr.some(x => x[0] === el[0] && x[1] === el[1])
}
The inner condition here could also be replaced by x.toString() === el.toString(). Then in the example above hasElement(a,[5,6]) returns true.
Is there a more elegant way to check the inclusion, preferably without writing helper functions?
You can use JSON.stringify method to convert the javascript object or value to a JSON string and then do the same for the array you want to search and just check if the main array includes the array you want to find.
const a = [[1,2],[5,6]], array = [5,6];
const find = JSON.stringify(a).includes(JSON.stringify(array));
console.log(find);
The reason is that in JavaScript, arrays are just objects and cannot be compared like values. Two object instances will never be equal, so even though they look identical to the eye, they are completely different objects and therefore will always be unequal. See:
console.log([5, 6] === [5, 6]) // false
The JavaScript Array class is a global object that is used in the construction of arrays; which are high-level, list-like objects.
You can try with find and destructure to simplify.
const a = [
[1, 2],
[5, 6]
];
const target = [5, 6];
// Method 1
const find = a.find(x => x.every(y => target.includes(y)));
// Method 2
const [xt, yt] = target;
const find2 = a.find(([x, y]) => xt === x && yt === y);
console.log(find, find2);
Referring back to Siva K V's answer. If you want to find the index of the first occurrence in the array where this is true, just replace find with findIndex (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)
const a = [
[1, 2],
[5, 6]
];
const target = [5, 6];
// Method 1
const find = a.findIndex(x => x.every(y => target.includes(y)));
// Method 2
const [xt, yt] = target;
const find2 = a.findIndex(([x, y]) => xt === x && yt === y);
console.log(find, find2);
I have this code in my vue-js app:
methods: {
onSubmit() {
ApiService.post('auth/sign_in', {
email: this.email,
password: this.password,
})
.then((res) => {
saveHeaderToCookie(res.headers);
this.$router.push({ name: 'about' });
})
.catch((res) => {
this.message = res.response.data.errors[0];
this.msgStatus = true;
this.msgType = 'error';
});
},
}
While running Eslint I got an error saying "Use array destructuring" (prefer-destructuring) at this line:
this.message = res.response.data.errors[0];
What is array destructuring and how to do this? Please provide me a concept on this. I've researched it but could not figure it out.
Destucturing is using structure-like syntax on the left-hand-side of an assignment to assign elements of a structure on the right-hand-side to individual variables. For exampple,
let array = [1, 2, 3, 4];
let [first, _, third] = array;
destructures the array [1, 2, 3] and assigns individual elements to first and third (_ being a placeholder, making it skip the second element). Because LHS is shorter than RHS, 4 is also being ignored. It is equivalent to:
let first = array[0];
let third = array[2];
There is also an object destructuring assignment:
let object = {first: 1, second: 2, third: 3, some: 4};
let {first, third, fourth: some} = object;
which is equivalent to
let first = object.first;
let third = object.third;
let fourth = object.some;
Spread operator is also permitted:
let [first, ...rest] = [1, 2, 3];
would assign 1 to first, and [2, 3] to rest.
In your code, it says you could do this instead:
[this.message] = res.response.data.errors;
The documentation on prefer-destructuring lays out what it considers to be "correct".
U can rewrite that line as [this.message] = res.response.data.errors; and that es-lint error will go off. See this example for better understanding
var x = {
y: {
z: {
w: [3, 4]
}
}
};
function foo() {
[this.a] = x.y.z.w
console.log(this.a);
}
foo() // prints 3
For more information about array destructuring please see here
Always look things up on MDN if you want to find out about javascript things. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Array_destructuring
Here's a simple example of destructuring:
const [a, b] = ['a', 'b'];
Its a shorthand available since es6 that allows doing variable assignment in a more shorthand way.
The original way would be like:
const arr = ['a', 'b'];
const a = arr[0];
const b = arr[1];
And the es6 way would be like:
const arr = ['a', 'b'];
const [a, b] = arr;
Now in regards to the eslint error, I actually disagree with that one. Your code by itself should be fine. So you should file an issue on the Eslint github repo to ask about why that line is triggering the "prefer-destructuring" warning.
Beside of the given destructuring assignments, you could take an object destructuring for an array if you like to take certain elements, like the 11th and 15th element of an array.
In this case, you need to use the object property assignment pattern [YDKJS: ES6 & Beyond] with a new variable name, because you can not have variables as numbers.
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
{ 11: a, 15: b } = array;
console.log(a, b);
Destructuring is a method of extracting multiple values from data stored in (possibly nested) objects and Arrays. It can be used in locations that receive data or as the value of objects. We will go through some examples of how to use destructuring:
Array Destructuring
Array destructuring works for all iterable values
const iterable = ['a', 'b'];
const [x, y] = iterable;
// x = 'a'; y = 'b'
Destructuring helps with processing return values
const [all, year, month, day] =
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.exec('2999-12-31');
Object Destructuring
const obj = { first: 'Jane', last: 'Doe' };
const {first: f, last: l} = obj;
// f = 'Jane'; l = 'Doe'
// {prop} is short for {prop: prop}
const {first, last} = obj;
// first = 'Jane'; last = 'Doe'
Examples of where to use Destructuring
// Variable declarations:
const [x] = ['a'];
let [x] = ['a'];
var [x] = ['a'];
// Assignments:
[x] = ['a'];
// Parameter definitions:
function f([x]) { ··· }
f(['a']);
// OR USE IT IN A FOR-OF loop
const arr = ['a', 'b'];
for (const [index, element] of arr.entries()) {
console.log(index, element);
}
// Output:
// 0 a
// 1 b
Patterns for Destructuring
There are two parties involved in any destructuring
Destructuring Source: The data to be destructured for example the right side of a destructuring assignment.
Destructuring Target: The pattern used for destructuring. For example the left side of a destructuring assignment.
The destructuring target is either one of three patterns:
Assignment target: Usually an assignment target is a variable. But in destructuring assignment you have more options. (e.g. x)
Object pattern: The parts of an object pattern are properties, the property values are again patterns (recursively) (e.g. { first: «pattern», last: «pattern» } )
Array pattern: The parts of an Array pattern are elements, the elements are again patterns (e.g. [ «pattern», «pattern» ])
This means you can nest patterns, arbitrarily deeply:
const obj = { a: [{ foo: 123, bar: 'abc' }, {}], b: true };
const { a: [{foo: f}] } = obj; // f = 123
**How do patterns access the innards of values? **
Object patterns coerce destructuring sources to objects before accessing properties. That means that it works with primitive values. The coercion to object is performed using ToObject() which converts primitive values to wrapper objects and leaves objects untouched. Undefined or Null will throw a type error when encountered. Can use empty object pattern to check whether a value is coercible to an object as seen here:
({} = [true, false]); // OK, Arrays are coercible to objects
({} = 'abc'); // OK, strings are coercible to objects
({} = undefined); // TypeError
({} = null); // TypeError
Array destructuring uses an iterator to get to the elements of a source. Therefore, you can Array-destructure any value that is iterable.
Examples:
// Strings are iterable:
const [x,...y] = 'abc'; // x='a'; y=['b', 'c']
// set value indices
const [x,y] = new Set(['a', 'b']); // x='a'; y='b’;
A value is iterable if it has a method whose key is symbol.iterator that returns an object. Array-destructuring throws a TypeError if the value to be destructured isn't iterable
Example:
let x;
[x] = [true, false]; // OK, Arrays are iterable
[x] = 'abc'; // OK, strings are iterable
[x] = { * [Symbol.iterator]() { yield 1 } }; // OK, iterable
[x] = {}; // TypeError, empty objects are not iterable
[x] = undefined; // TypeError, not iterable
[x] = null; // TypeError, not iterable
// TypeError is thrown even before accessing elements of the iterable which means you can use empty Array pattern [] to check if value is iterable
[] = {}; // TypeError, empty objects are not iterable
[] = undefined; // TypeError, not iterable
[] = null; // TypeError, not iterable
Default values can be set
Default values can be set as a fallback
Example:
const [x=3, y] = []; // x = 3; y = undefined
Undefined triggers default values
I'm trying to fully understand functional programming when sorting an array why does the original array change to the sorted array also? I
want to check if the array is in ascending order.
let arr = [1,2,8,3,9,88,67];
let newArr = arr.sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
I want to do something like....
if (arr === newArr) {
return true;
} else {
return false;
}
The original arr is also being sorted so it always is true,
some guidance would be great thanks.
That's just the way the sort operator was designed. It modifies the existing array, rather than creating a new array. If you want to create a new array, you can do it like this:
let arr = [1,2,8,3,9,88,67];
let newArr = arr.slice(); // creates a copy of the array
newArr.sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
Javascript array is an object, When 2 variables reference the same object, changing one would change the value of the other
let obj1 = {x:1, y:2};
let obj2 = obj1;
obj2.x = 3;
console.log(obj1);
You can sort it using
let arr = [1,2,8,3,9,88,67];
let newArr = arr.slice().sort((a,b) => a-b);
console.log(newArr);
console.log(arr);
By assigning an object, you take the reference of the object, that means ecery change affects the same object.
For arrays, you could take "a shallow copy" of it with Array#slice and then map the check of the items.
var array = [1, 2, 8, 3, 9, 88, 67],
sorted = array.slice().sort((a, b) => a - b),
position = array.map((a, i) => a === sorted[i]);
console.log(sorted);
console.log(position);
.as-console-wrapper { max-height: 100% !important; top: 0; }
According to your question,
you just need to identify
If the array is in ascending order
To do that just apply some simple logic and we do not want to compare it with a sorted array.
This is the speediest method since it will break the loop on wrong condition.
let arr = [1,2,8,3,9,88,67];
let is_sorted = true;
for(let i=0, length = arr.length; i < length - 1; i++){
if(Number(arr[i]) > Number(arr[i+1])){
is_sorted = false;
break;
}
}
console.log("Is Sorted: ", is_sorted);
This is an XY problem – ie, you want "... to check if the array is in ascending order" and you tried sorting (via Array.prototype.sort) and then you're checking the result using binary operator ==. The result isn't what you expect, so you ask about the sort instead of keeping the focus on your actual goal: checking for ascending order
A case for why you shouldn't sort
Imagine a large array of hundreds or thousands or items. Is the following array in ascending order?
isAscending ([ 5, 1, ... thousands more items ... ])
// => true or false ?
Of course it's false because 1 is less than 5; the array is not in ascending order. Did we have to sort the entire array to arrive at that answer? No, we only needed to look at the first 2 items (in this case) to know the answer is false
Other answers show using .slice to copy the input array – this is silly tho, because of course we don't have to copy an array to determine if it is in ascending order – it's a waste of time/space.
A case for why you shouldn't use ==
Well first, you can't, because of the following (in JavaScript)
[ 1, 2, 3 ] == [ 1, 2, 3 ]
// => false
So how would use == if you could? Maybe an approach would be to check if each element in the first array is equal to each array element in the second array. Ok, now imagine two large arrays of hundreds of thousands of items. Are the following two arrays equal?
[ 1, ... thousands of items ... ] == [ 2, ... thousands of items ... ]
// => true or false ?
We know they're not equal because 1 != 2 – it's the same case as the sorting; there's no sense in comparing the rest of the items because we already know the arrays are not equal
There's other ways to compare Arrays (and objects) in JavaScript, but we only have one input array, so there's no array to compare it to – this is another dead-end to the approach
Check if an array is in ascending order
Ok, so now that were done talking about array sorting and array equality in JavaScript, we can actually write a function that does what you intend it to do
const isAscending = ([x,y,...rest]) =>
x === undefined // array is empty
? true
: y === undefined // single element array
? true
: x > y // first element is larger than second
? false
: isAscending ([y,...rest]) // check remaining elements
console.log (isAscending ([])) // true
console.log (isAscending ([1])) // true
console.log (isAscending ([1,3,5,7])) // true
console.log (isAscending ([5,1,3,7])) // false
Stack-safe recursion
But you have to be careful using recursion in JavaScript – input arrays of just a couple thousand elements could cause a stack overflow in the program above
Using a constant-space looping mechanism, we can rewrite isAscending in a way that works on arrays of any input size. The loop/recur interface gives us an opportunity to track any number/type of state variables, so this implementation also avoids the costly creation of many array slices (in rest parameter and spread argument syntax above)
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const isAscending = xs =>
xs.length < 2
? true
: loop ((a = 0, b = 1) =>
a === xs.length
? true
: xs [a] > xs [b]
? false
: recur (a + 1, b + 1)) // *
console.log (isAscending ([])) // true
console.log (isAscending ([1])) // true
console.log (isAscending ([1,3,5,7])) // true
console.log (isAscending ([5,1,3,7])) // false
* or recur (b, b + 1), which saves on addition operation per array element
The sort method applied to an array will modify the array itself. So it's logic to have the first array sorted and both arr and newArr will be equal.
To test if the array is in ascending order you may loop through it and check if there is and index i where arr[i+1] < arr[i]
let arr = [1,2,8,3,9,88,67];
let test=true;
for (var i = 1; i < arr.length; i++) {
if(arr[i]<arr[i-1]) {
test=false;
break;
}
}
console.log(test);
I will go straight to code on this one because it is very specific. How can I test if arr contains an element which is itself a specific array. Why can I not use an array literal as an arg to indexOf? I'm trying to see if an array of coordinates contains a specific coordinate pair.
var arr = [[0,0], [1,1]];
arr[0]; // [0, 0]
arr.indexOf([0, 0]); // -1
var y = arr[0];
arr.indexOf(y); // 0
var x = [0, 0];
arr.indexOf(x); // -1
As others have pointed out, [0, 0] !== [0, 0]. Instead, use findIndex with a function which checks array equality:
var match = [0, 0];
array.findIndex(function(elt) { return arrayIsEqual(elt, match); })
Now you just have to write arrayIsEqual. See How to check if two arrays are equal with JavaScript? for some ideas. If your arrays always have two elements, then it could be just elt[0] === match[0] && elt[1] === match[1].
Obligatory disclaimer: findIndex is ES6. However, it's implemented almost everywhere you would care about, except apparently IE11. If need be, use a polyfill or write your own.
"Why can I not use an array literal as an arg to indexOf?"
I beleive the problem here is that an Array is an object. You cannot create a new object
var x = [0, 0];
and expect it to match arr[0]; because it is not the same object in memory.
To demonstrate I think this would work, but I havent tested it:
var arr = [[0,0], [1,1]];
var x = [0, 0];
arr[0] = x;
arr.indexOf(x); // 0
We can use instanceOf to check the type of variable or value
if(value instanceOf Array)
{
//value is of array type
}
and if you want to compare it with some specific array try below code in if statement
var is_same = (array1.length == array2.length) && array1.every(function(element, index) {
return element === array2[index];
});
if is_same is true then array is identical
Passing objects into the array#indexOf method might not give the results you expect
The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
Hopefully, someone else can confirm this.
Sounds like the perfect use for array.prototype.some
function hasCoordinates(array, coords) {
return array.some(function(element, coords) {
return Array.isArray(element) && String(array[element]) == String(coords);
});
}
hasCoordinates([1,3,[4,6]], [4,6])
=> should return true
We can use the isArray method to determine if an object is array.
It should be noted that the some function is only available in ie9 and up
To find the exact coordinates, you can not compare arrays for equality, as they are treated as different objects.
Ex.
[0,0] == [0,0]
=> false
We need to perform type conversion first
String[0,0] == String[0,0]
=> true
This is because, now the arrays are being evaluated as strings.
You must use a comparable type to use .indexOf(). When you use a comparison operator with objects (and Arrays are objects) then JS uses reference comparison (MDN Docs). It is probably not what you want to do but you can use a reference as I will show below:
var a = [0, 0];
var b = [1, 1];
var c = [1, 1];
var e = c; // e now has the same reference as c
console.log(b == c); // false - Reference comparison is used here
console.log(c == e); // true - References are the same
var d = [a, b, c];
console.log(d.indexOf(a)); // 0
console.log(d.indexOf(b)); // 1
console.log(d.indexOf(c)); // 2
console.log(d.indexOf(e)); // 2
If you create 2 objects with the same values inside they still do not have the same reference (like b and c in the above code). As mentioned by #torazaburo you could instead use the .findIndex() function.
You can do this something like below where you pass in your array to find and it returns a function which returns true when it matches each element.
var a = [0, 0],
b = [0, 0],
c = [0, 1];
var d = [a, b, c];
function equalArrays(a) {
return function(b) {
if (a.length != b.length)
return false;
for (var i = 0, len = a.length; i < len; i++) {
if (a[i] != b[i])
return false;
}
return true;
}
}
console.log(d.findIndex(equalArrays([0, 0]))); // 0 - index of FIRST array matching [0,0]
console.log(d.findIndex(equalArrays([0, 1]))); // 2 - index of an array matching [0,1]
console.log(d.findIndex(equalArrays([1, 1]))); // -1 - No match found
To answer the last question "Why can I not use an array literal as an arg to indexOf?" the answer is due to how indexOf is defined.
Refer to the ECMAScript 2015 Array.prototype.indexOf spec: http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.indexof
indexOf compares searchElement to the elements of the array, in ascending order, using the Strict Equality Comparison algorithm (7.2.13), and if found at one or more indices, returns the smallest such index; otherwise, −1 is returned.
The important part is the algorithm used; Strict Equality Comparison. In other words, triple equals.
Consider this JS:
var a = [1, 2];
var b = a === [1, 2]; // true or false?
What is the value of b? It's false. If you want to learn more about why that is the case, I recommend reading https://stackoverflow.com/a/14853974/1022333.