Related
I got a problem with my jest/chai unit test.
As you can see I received the same output but the test failed with message: "compared values have no visual difference". How can I fix this?
This is common in javascript. As you probably know already, an array is an object, so I would be explaining using objects.
TLDR: Variable assigned to an object is only a pointer to such value in memory. Hence it is impossible for two variables despite having the different objects containing the same PHYSICAL value but not referring to the same address in memory to be equal.
Javascript compares Objects by identity which means that for the case of the objects a and b below:
Example 1:
a = {name: 'tolumide'}
b = {name: 'tolumide'}
a === b // False
a !== b //True
Although a and b have the same content, there identity differs hence =>
a !== b //True
However, this is different from example 2 below:
Example 2:
c = {name: 'tolumide'}
c = d
c === d // True
c !== d // False
This is because c and d refer to the same value in memory.
What you need is for testing in jest would be something like this:
const expected = ['Alice', 'Bob', 'Eve']
expect.arrayContaining(array)
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
OR
const shoppingList = ['beer', 'whiskey']
expect(shoppingList).toContain('beer');
Jest source
Forgive me if I state anything you already know, but the issue appears to be in the underlying object comparison performed by jest.
Array's in javascript are just objects with numbered properties, i.e.
{
0: "value at first index",
1: "value at second index"
}
As such the comparison is done on this underlying object. According to this issue raised by someone on the jest github page, it is possible that the function you are using to create the sorted array is returning a none standard array back, so the comparison fails on the underlying object.
Similar looking issue:
https://github.com/facebook/jest/issues/5998
The solution suggested by one commenter is to Json.Stringify() each array in the test and perform a comparison on that, as this will compare the values themselves.
This is however, as stated "A work around not a fix".
Edit
The stringify option does not work in all cases and should be avoided.
The complete solution for testing if two arrays are equal is found in the first answer to this question: How to check if two arrays are equal with JavaScript?
Disclaimer: Not my code!
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 (i.e. [1, 2, 3] == [3, 2, 1]), 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;
}
I have an angular page, with input fields from which I read input values and set them in variables inside .ts file. The values entered may change, so I cant hard-code values.
These are the variables I set values in using [(ngModel)]. All is good with that, values are set.
input1_1: string;
input1_2: string;
input2_1: string;
input2_2: string;
Then I want to make comparison. All the inputs ending with _1 is put in array fromArray, all other, ending with _2 in toArray.
Then I want to make comparison, because I don't want to have a value from fromArray inside toArray.
In a function I tried doing
if (this.toArray.includes(this.input1_1) == true) {
//then disable next button
}
But it doesn't work. I guess I need to get actual values, because it doesn't seem to be checking for actual values.
How can I manage to check if array of variables has same value as a different variables value?
This is all code together (sample from it, as its checking for other things also in the if statement), should make it clearer.
// input values from angular html input fields
input1_1: string;
input1_2: string;
input2_1: string;
input2_2: string;
input3_1: string;
input3_2: string;
input4_1: string;
input4_2: string;
// arrays for checking
fromArray = [this.input1_1, this.input2_1, this.input3_1, this.input4_1];
toArray = [this.input1_2, this.input2_2, this.input3_2, this.input4_2];
// function to run on each input field
checkValues() {
if (this.toArray.includes(this.input1_1) == true) {
this.disableNext = true
} else (this.disableNext = false)
}
I could have just compared values to each other, but I did not want to write too much code, thought this should be a lot shorter.
I've spent last 2 hours searching for similar examples, but none helped.
I did read about array.some(), but It seems that it wouldnt help me, and would cause extra code write, and array.includes() is a better option.
Any tip would be great.
I'm new to coding, so please be gentle and ask for clarification if needed.
EDIT:
As requested by Jo Carrasco and Dev - I will input the whole thing:
StackBlitz link
The html formatting is totally wrong in stackblitz, but it does what is needed.
The this.toArray.includes(this.input1_1) is only in first dropdown function (checkDropDown1) for now, I dont see need to add to others, as I need to check for only one now.
you can see the logic of disabling Next button also - all values needs to be entered if checkbox is checked, and if they are same in either of the places (either 2 dropdowns are same, or any of input fields), the button needs to be disabled.
EDIT 2
It seems that it doesn't update the values in array once they are dynamically changed. It takes the first value the variable had, and saves that value in array, but doesn't change it when and edit happens on the variable (input field).
-- What I did to fix it is I am splicing array value, so that whenever I call a function which should check the values in array, I am deleting and then inserting that value that I need to update into the array and then do a comparison using if statement inside for. Then I do the check inside the actual if statement with equalValue where the original array.include(); was supposed to be.
this.fromArray.splice(0, 1, this.input1_1);
this.toArray.splice(0, 1, this.input1_2);
var equalValue = false;
for (let i = 0; i < this.toArray.length; i++) {
if( this.toArray[i] === this.input1_1) {
equalValue = true;
console.log(this.toArray[i]);
console.log(this.input1_1);
break;
}
}
If you have a better solution, please write it down, I will go through it and mark as answer if it solves the issue better.
Please show the actual code, so we can run it by ourselves.
It looks like those string properties are undefined.
undefined == true //false
So, that's why you can't get the actual value
Try to do it like this
if (this.toArray.includes(this.input1_1)) {
if you really dont care about exact value but toArray and fromArray should not have any value same then here is your solution-
include loadash in your project loadash - https://lodash.com/docs/4.17.15#uniq
checkValues() {
if (_.uniq([...this.toArray, ...this.fromArray]).length < [...this.toArray, ...this.fromArray].length ) {
this.disableNext = true
} else (this.disableNext = false)
}
explanation -
if both array have some values same-
this.toArray = [a,b,c];
this.toArray = [a,b,d];
[...this.toArray, ...this.fromArray] will equal to [a,b,c,a,b,d] // length will be 6
_.uniq([...this.toArray, ...this.fromArray]) will equal to - [a,b,c,d]; // length will be 4
// so if duplicates is present then left hand will be less than right hand in if statement
if both array have no values same
this.toArray = [a,b,c];
this.toArray = [d,g,f];
[...this.toArray, ...this.fromArray] will equal to [a,b,c,d,g,f] // length will be 6
_.uniq([...this.toArray, ...this.fromArray]) will equal to - [a,b,c,d,g,f]; // length will be 6
// so in this case it will go to false condition
hope it helps Happy coding !! :)
What I did to fix it is I am splicing array value, so that whenever I call a function which should check the values in array, I am deleting and then inserting that value that I need to update into the array and then do a comparison using if statement inside for. Then I do the check inside the actual if statement with equalValue where the original array.include(); was supposed to be.
checkValues() {
this.fromArray.splice(0, 1, this.input1_1);
this.toArray.splice(0, 1, this.input1_2);
var equalValue = false;
for (let i = 0; i < this.toArray.length; i++) {
if( this.toArray[i] === this.input1_1) {
equalValue = true;
console.log(this.toArray[i]);
console.log(this.input1_1);
break;
}
}
if (this.equalValue == true) {
this.disableNext = true
} else (this.disableNext = false)
}
If you have a better solution, please write it down, I will go through it and mark as answer if it solves the issue better.
This question already has answers here:
Javascript: Using `.includes` to find if an array of objects contains a specific object
(7 answers)
Closed 23 days ago.
I'm attempting to find an object in an array using Array.prototype.includes. Is this possible? I realize there is a difference between shallow and deep comparison. Is that the reason the below code returns false? I could not find a relevant answer for Array.includes().
Array.includes compares by object identity just like obj === obj2, so sadly this doesn't work unless the two items are references to the same object. You can often use Array.prototype.some() instead which takes a function:
const arr = [{a: 'b'}]
console.log(arr.some(item => item.a === 'b'))
But of course you then need to write a small function that defines what you mean by equality.
Its' because both of the objects are not the same. Both are stored at different place in memory and the equality operation results false.
But if you search for the same object, then it will return true.
Also, have a look at the below code, where you can understand that two identical objects also results false with the === operator.
For two objects to return true in ===, they should be pointing to same memory location.
This is because the includes checks to see if the object is in the array, which it in fact is not:
> {a: 'b'} === {a: 'b'}
false
This is because the test for equality is not testing if the objects are the same, but whether the point to the same object. Which they don't.
You are on the right track but, the issue is the difference between reference and value types, you currently are using a reference type (object literal), so when you compare what is in the array with what you have, it will compare the references and not the values. this is what I mean:
var ar = [];
var x = {a: "b", c: "d" };
ar.push(x);
// this will log true because its the same reference
console.log("should be true: ", ar[0] === x);
ar.push({a: "b", c: "d" });
// this will log false because i have created
// a different reference for a new object.
console.log("should be false: ", ar[1] === x);
// Think of it like this
var obja = { foo: "bar" }; // new reference to 'obja'
var objb = { foo: "bar" }; // new reference to 'objb'
var valuea = 23;
var valueb = 23;
// 'obja' and 'obja' are different references
// although they contain same property & value
// so a test for equality will return false
console.log("should be false: ", obja === objb);
// on the other hand 'valuea' and 'valueb' are
// both value types, so an equality test will be true
console.log("should be true: ", valuea === valueb);
to achieve what you want, you either have to have added the actual reference, as I did above, or loop through the array and compare by unique property of the objects.
you can use find to returns the value of the this element
const array = [{a: 'b'}];
array.includes(array.find(el=>el.a==='b'));
const arr = [{a: 'b',b:'c'},{a: 'c'}]
console.log(arr.some(item => item.b === 'c'))
Yes, you can, if only so
const arr = [{a:'a'},{a: 'b'}]
const serch = arr[1]
console.log(arr.includes(serch))
You cannot use string comparisons on objects unless you first convert them to strings with...
JSON.stringify(TheObject)
So to loop through all objects in an array so as to prevent adding a duplicate object, you can use something like this...
let Bad=false;
ObjectArray.forEach(function(e){if(JSON.stringify(NewObject)===JSON.stringify(e)){Bad=true;}});
if(Bad){alert('This object already exists!');} else {ObjectArray.push(NewObject);}
self.scope.savesectioneditmain = [self.scope.savesection2details,
self.scope.editsection2,
self.scope.savesection1details,
self.scope.editsection1];
Based on above codes, I want to check all array value equal to true value.
if($.inArray(true, self.scope.savesectioneditmain) == 0)
I have tried $inArray, but $inArray checks only one condition, but I want to check all array value should be equal to true.
You can use Array.prototype.every()
The every() method tests whether all elements in the array pass the test implemented by the provided function.
if(self.scope.savesectioneditmain.every(x => x == true)){
//All elements are true
}
I'm working with Swift, SpriteKit and Xcode 6,
I have an array of a SKSpriteNode class :
class EnemyVehicle: SKSpriteNode {
}
var vehicles = [EnemyVehicle]()
So I can add elements to this array like this:
vehicles.append(EnemyVehicle(imageNamed:"mySprite"))
And the superclass SKSpriteNode contains an attribute which is position.y, and I can access it in every element of my array with vehicle[x].position.y.
My question is: is it possible to test every specific attribute in an array (here position.y) and watch if it match the conditions? For example, if I want a condition which is true only when all of the position.y values of all my elements in my array are superior than 0? I know I could do it with a loop, but is there another easier way to do it?
Sounds like you want reduce.
Reduce takes a starting value, and then a closure that takes the running value and the next element of the array, and combines these two elements together, carrying that result forward to combine with the next element.
For example:
let a = [1,2,3]
// starting with zero, add each element to a total
let sum = a.reduce(0) { total, i in total + i }
// sum = 6
To check if all elements in an array match a given criteria, you want a combining function that checks the property and returns true or false, anding that with the previous criteria:
// assuming you’re ok for true for an empty array:
let allAboveGround = vehicles.reduce(true) {
previous, vehicle in
previous && vehicle.position.y > 0
}
There’s one downside to this – as soon as you hit a value that is false, you could stop right there because the overall value cannot be true. Reduce doesn’t cater for this so you may want to write a loop, or maybe an all function that wraps a loop, that quits early under these conditions:
func all<S: SequenceType>(source: S, pred: S.Generator.Element->Bool) -> Bool {
for x in source {
if !pred(x) { return false }
}
return true
}
let allAboveGround = all(vehicles) { $0.position.y > 0 }