Why is this test failing? Array should equal [] [duplicate] - javascript

This question already has answers here:
chai test array equality doesn't work as expected
(7 answers)
Closed 6 years ago.
My test, below the alerts var is an empty array [] however this throws an error.
it('FeedFactory alerts array is filled', function() {
var alerts = FeedFactory.returnAlerts();
console.log('alerts',alerts);
expect(alerts).to.equal([]);
});
Here is the test log when I comment out the last expect(alerts).to.equal([]); line.
Code from my Factory
var alerts = [];
var feedFactory = {
alerts : alerts,
getFeed : getFeed,
returnAlerts : returnAlerts,
saveFilters : saveFilters,
getFilters : getFilters
};
function returnAlerts() {
return alerts;
}

Because [] !== [] && [] != []. JS objects are not loosely or strictly equal to one another, so expect([]).to.equal([]) will always fail.
You should use a deeper form of equality, such as deep (or its confusing shorthand, eql) or members:
expect([1, 2, 3]).to.deep.equal([1, 2, 3])
expect([1, 2, 3]).to.include.members([1, 2])

Two empty arrays are not equal to each other because each has a different reference, since all objects, (and arrays for that matter) are referenced and not copied. The checking for equality on objects are checking on reference and not internal content
hance:
var a = [];
var b = [];
console.log(a == b) //false
i would use the length of the alerts array inorder to test if its an empty array. by writing:
expect(alerts.length).to.equal(0);
in order to check the content of the array to equal another content of another array, you must use deep checking, which checks for each value of the array to equal the one in the other array (quite expensive).

Related

Add non duplicates numbers into an Array [duplicate]

This question already has answers here:
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 5 years ago.
I wrote a function in javaScript that checks if it is the first time the numbers has been seem, if so, it will be added to the the array. For some reason the first number is always repeating it, Ex:
if we pass [1,1,1,2,3,3,4,5]
it will print out [1,1,2,3,4,5] instead of [1,2,3,4,5]
Could anyone tell me what I'm missing ??
Thank you in advance.
var numbers = [1,1,1,1,1,2,3,4,4,4,5,5,6,6,6,8];
function eliminateDup(arr){
var myArr = [];
for(var i = 0;i<arr.length;i++){
if(!myArr[arr[i]]){
myArr.push(arr[i]);
}
}
return myArr;
}
console.log(eliminateDup(numbers));
This comparison doesn't make sense :
if(!myArr[arr[i]]){
It is true only if !myArr[arr[i]] can be converted to true.
You get twice 1 in the target array myArr because both arr[0] and arr[1] equals to 1 and myArr has an undefined element at the 1 index for the two first iterations at the time where the conditional statement is executed.
First iteration
i = 0
arr[0] = 1
myArr[1] = undefined
So if(!myArr[1]) is true.
You add 1.
Second iteration
i = 1
arr[1] = 1
myArr[0] = 1 // now valued but not used in the test
myArr[1] = undefined;
So if(!myArr[1]) is true.
You still add 1.
Third iteration
i = 2
arr[2] = 1
myArr[0] = 1
myArr[1] = 1
So if(!myArr[1]) is false.
nothing is added.
You should rather check whether the current value is not contained in the target array before adding it in the target array :
if(myArr.indexOf(arr[i])==-1){
myArr.push(arr[i]);
}
use this condition instead, for push
if(myArr.indexOf([arr[i])===-1)
myArr.push(arr[i]); will push to back of the array myArr (just like a stack) and thus element '1' will be at index 0.
myArr[arr[i]] checks if element at index arr[i] exists which it won't so will push another 1.
As suggested above use indexof() which will just search and give an index if the element exists.

Unusual if/else behaviour [duplicate]

This question already has answers here:
How to compare arrays in JavaScript?
(55 answers)
Closed 5 years ago.
I have an array of two nested arrays.
var array = [ [a,b,c], [a,b,c] ]
Despite the array elements being identical, the following code returns true:
if (array[0] !== array[1]) {
console.log(array[0])
console.log(array[1])
}
// [a,b,c]
// [a,b,c]
And the following code returns false:
if (array[0] === array[1]) {
console.log(array[0])
console.log(array[1])
}
It seems to be comparing the indices instead of the elements.
What is going on here?
Sometimes I will be comparing 3 or possibly even 4 nested arrays to each other. For instance, if ( array[0] === array[1] || array[0] === array[2] || array[1] === array[2] ) // do this. Notably, a and c will always be references to actual HTML elements, whereas b will be a number. Is there not a simple way to accomplish this nowadays?
You are comparing object references, not object values. The pointers to memory are different, and as a result the comparison is false.
Here is a simple example using html elements in the arrays.
var a1 = document.querySelectorAll('div');
var a2 = document.querySelectorAll('div');
var a3 = document.querySelectorAll('div');
var array = [Array.from(a1),Array.from(a2),Array.from(a3)];
console.log(array[0].every((v,i) => array.slice(1).every(ai => v == ai[i])));
<div>1</div><div>2</div><div>3</div>

What is the difference between `var in array` and `array.indexOf(var)`?

I am trying to get my head around arrays in JS. My question is; are the following two tests equivalent?
var test = 2;
console.log(test in [1,2,3]);
console.log([1,2,3].indexOf(test) != -1);
They seem to be, but then answers like this and the book I am reading suggest that you can't do in on an array, only on an object. Looking for clarity. There must be a reason that people use .indexOf(x) (which I assume is linear time) and not in (which I assume is constant time).
No. They are completely different.
test in [1,2,3] checks if there is a property named 2 in the object. There is, it has the value 3.
[1,2,3].indexOf(test) gets the first property with the value 2 (which is in the property named 1)
suggest that you can't do in on an array, only on an object
Arrays are objects. (A subclass if we want to use classical OO terminally, which doesn't really fit for a prototypal language like JS, but it gets the point across).
The array [1, 2, 3] is like an object { "0": 1, "1": 2, "2": 3 } (but inherits a bunch of other properties from the Array constructor).
As per MDN,
The in operator returns true if the specified property is in the specified object.
in will check for keys. Its similar to Object.keys(array).indexOf(test)
var a1 = [1,2,3];
var a2 = ['a', 'b', 'c']
var test = 2;
console.log(test in a1)
console.log(test in a2)
// Ideal way
//ES5
console.log(a1.indexOf(test)>-1)
console.log(a2.indexOf(test)>-1)
//ES6
console.log(a1.includes(test))
console.log(a2.includes(test))
The first checks for an index, or if property of an object exists,
console.log(test in [1,2,3]);
and not for a value in the array, as the second is doing.
console.log([1,2,3].indexOf(test) != -1);
The in operator returns true if the specified property is in the
specified object.
Using in operator for an array checks of the indices and the length property - because those are the properties for an array:
console.log(Object.getOwnPropertyNames([1, 2, 3]));
console.log(Object.keys([1, 2, 3]));
console.log('length' in [1, 2, 3]);
Object.keys : return all enumerable properties
Object.getOwnPropertyNames : return all properties
Try this
var test = 2;
var arrval= [1, 5, 2, 4];
var a = arrval.indexOf(test);
if(a>-1) console.log("Having");
else console.log("Not Having");

Sparse arrays and reduce in JavaScript

One would think that in JavaScript:
var array = [1,2,undefined,4];
is the same as:
var array = [1,2];
array.length = 3;
array.push(4);
but it's not. This code shows it:
var array1 = [1,2];
array1.length = 3;
array1.push(4);
var array2 = [1,2,undefined,4];
traverseArray(array1);
traverseArray(array2);
function traverseArray(array) {
console.log("trying: " + array);
console.log("reduce:");
array.reduce(function(prev, current, index, array) {
if(current === undefined) {
console.log("Found undefined");
}
console.log(index+": " +current);
}, undefined);
console.log("for loop:")
for(var i=0;i < array.length;i++) {
var current = array[i];
console.log(i+": " +current);
}
console.log();
}
Output:
trying: 1,2,,4
reduce:
0: 1
1: 2
3: 4
for loop:
0: 1
1: 2
2: undefined
3: 4
trying: 1,2,,4
reduce:
0: 1
1: 2
Found undefined
2: undefined
3: 4
for loop:
0: 1
1: 2
2: undefined
3: 4
Why is undefined in array1 not the same as undefined in array2 and why does the for loop act the same but reduce does not?
array1 has three numerically-named properties: 0, 1, and 3.
array2 has four numerically-named properties: 0, 1, 2, and 3. The value of the property named 2 happens to be undefined.
When you ask an object for the value of a property it doesn't have, the result is undefined.
In the for loop, you ask each array for the values of its properties named 0, 1, 2, and 3. For array1, the property named 2 does not exist, so the property access produces undefined. For array2, the property does exist, but its value actually is undefined, so you get the same result.
On the other hand, reduce only operates on properties that actually exist. From the ECMAScript specification, this is how reduce loops over arrays, using a counter k:
Repeat, while k < len
Let Pk be ToString(k).
Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
If kPresent is true, then... [use the value at index k for the reduce call]
So, we can see that an index is only used if passes a [[HasProperty]] check. array1 does not have a property named 2, so that index is skipped.
apsillers nailed it in the comments . . . the difference is that in array2 you have actually assigned an undefined value to the 3rd element in the array (i.e., at index 2), where as, in array1, you have two elements initially, change the length property, and then add a third element in the forth position.
Here are the relevent sections from MDN that explains why the distinction is important:
From the page on Array.length:
When you extend an array by changing its length property, the number of actual elements does not increase; for example, if you set length to 3 when it is currently 2, the array still contains only 2 elements. Thus, the length property says nothing about the number of defined values in the array.
From the page on Array.push():
The push method relies on a length property to determine where to start inserting the given values.
The key here, is really that the length property is really, simple a property that is in no way inherantly tied to the contents of the array. It is simply because the various methods of Array also happen to maintain that property, as they "do their work", that I behaves as if it is.
So, as a result, in array2, the code is actually reporting back the undefined value that you assigned to array2[2], whereas, with array1, the code is interpreting the absence of a value at array1[2] as undefined.

Arrays created with new? [duplicate]

This question already has answers here:
Undefined values in Array(len) initializer
(5 answers)
Closed 7 years ago.
I am confused by the results of mapping over an array created with new:
function returnsFourteen() {
return 14;
}
var a = new Array(4);
> [undefined x 4] in Chrome, [, , , ,] in Firefox
a.map(returnsFourteen);
> [undefined x 4] in Chrome, [, , , ,] in Firefox
var b = [undefined, undefined, undefined, undefined];
> [undefined, undefined, undefined, undefined]
b.map(returnsFourteen);
> [14, 14, 14, 14]
I expected a.map(returnsFourteen) to return [14, 14, 14, 14] (the same as b.map(returnsFourteen), because according to the MDN page on arrays:
If the only argument passed to the Array constructor is an integer
between 0 and 2**32-1 (inclusive), a new JavaScript array is created
with that number of elements.
I interpret that to mean that a should have 4 elements.
What am I missing here?
When you create an array like so:
var arr1 = new Array( 4 );
you get an array that has a length of 4, but that has no elements. That's why map doesn't tranform the array - the array has no elements to be transformed.
On the other hand, if you do:
var arr2 = [ undefined, undefined, undefined, undefined ];
you get and array that also has a length of 4, but that does have 4 elements.
Notice the difference between having no elements, and having elements which values are undefined. Unfortunately, the property accessor expression will evaluate to the undefined value in both cases, so:
arr1[0] // undefined
arr2[0] // undefined
However, there is a way to differentiate these two arrays:
'0' in arr1 // false
'0' in arr2 // true
var a = new Array(4);
This defines a new array object with an explicit length of 4, but no elements.
var b = [undefined, undefined, undefined, undefined];
This defines a new array object with an implicit length of 4, with 4 elements, each with the value undefined.
From the 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.
For array a, there are no elements that have been assigned values, so it does nothing.
For array b, there are four elements that have been assigned values (yes, undefined is a value), so it maps all four elements to the number 14.
new Array(len) creates an empty array, and does something different than filling it with undefined values: It sets its length to len. So, it translates to this code:
var newArr = [];
newArr.length = len;
Let's have some fun with newArr (assuming that len = 4):
newArr.length; //4
newArr[1] === undefined; //true
newArr.hasOwnProperty(1); //false
This is because while the is 4 items long, it does not contain any of these 4 items. Imagine an empty bullet-clip: It has space for, say, 20 bullets, but it doesn't contain any of them. They weren't even set to the value undefined, they just are...undefined (which is a bit confusing.)
Now, Array.prototype.map happily walks along your first array, chirping and whistling, and every time it sees an array item, it calls a function on it. But, as it walks along the empty bullet-clip, it sees no bullets. Sure, there are room for bullets, but that doesn't make them exist. In here, there is no value, because the key which maps to that value does not exist.
For the second array, which is filled with undefined values, the value is undefined, and so is the key. There is something inside b[1] or b[3], but that something isn't defined; but Array.prototype.map doesn't care, it'll operate on any value, as long as it has a key.
For further inspection in the spec:
new Array(len) : http://es5.github.com/#x15.4.2.2
Array.prototype.map : http://es5.github.com/#x15.4.4.19 (pay close attention to step 8.b)
One additional answer on the behavior of console.log. Plain simple, the output is sugar and technically wrong.
Lets consider this example:
var foo = new Array(4),
bar = [undefined, undefined, undefined, undefined];
console.log( Object.getOwnPropertyNames(bar) );
console.log( Object.getOwnPropertyNames(foo) );
As we can see in the result, the .getOwnPropertyNames function returns
["length"]
for the foo Array/Object, but
["length", "0", "1", "2", "3"]
for the bar Array/Object.
So, the console.log just fools you on outputting Arrays which just have a defined .length but no real property assignments.

Categories