How to sort based on incomplete criteria? - javascript

First I tried passing my own function to Array.sort, but it doesn't sort correctly. Notice how 'c' comes before 'a' in the result, even though the case if (b == 'a' && a == 'c') is handled correctly.
These data are just for example. My actual data is not to be alphabetically sorted. It must use the logic illustrated in the a_before_b and b_before_a functions.
Since I only have conditions to determine the relative ordering of SOME (NOT all) pairs of elements, there may be multiple valid orderings of elements. I just need to produce ANY valid ordering, where valid means does not contradict any of my conditions (which are defined in the a_before_b and b_before_a functions).
const sorted = ['a', 'b', 'c', 'd']; // I do NOT have access to this
const unsorted = ['c', 'd', 'a', 'b'];
const a_before_b = (a, b) => {
if (a == 'a' && b == 'd') return true;
if (a == 'b' && b == 'c') return true;
}
const b_before_a = (a, b) => {
if (b == 'a' && a == 'c') return true;
if (b == 'b' && a == 'c') return true;
}
const mySortingFunction = (a, b) => {
if (a_before_b(a, b)) return -1;
if (b_before_a(a, b)) return 1;
return 0;
}
// doesn't produce correct sorting
console.log(unsorted.sort(mySortingFunction)); // [ 'c', 'a', 'd', 'b' ]
Then I tried writing my own sort from scratch. But it enters an infinite loop and I don't know why.
const sorted = ['a', 'b', 'c', 'd'];
const unsorted = ['c', 'd', 'a', 'b'];
const a_before_b = (a, b) => {
if (a == 'a' && b == 'd') return true;
if (a == 'b' && b == 'c') return true;
}
const b_before_a = (a, b) => {
if (b == 'a' && a == 'c') return true;
if (b == 'b' && a == 'c') return true;
}
const findAnUnsortedElement = array => {
for (let [i, element] of Object.entries(array)) {
i = +i;
const a = element;
const b = array[i + 1];
if (b === undefined) return 'SORTING_COMPLETE';
if (!a_before_b(a, b)) console.log(a, 'should not be before', b);
if (b_before_a(a, b)) console.log(b, 'should be before', a);
if (!a_before_b(a, b) || b_before_a(a, b)) return a;
}
}
// from w3schools
function move(arr, old_index, new_index) {
while (old_index < 0) {
old_index += arr.length;
}
while (new_index < 0) {
new_index += arr.length;
}
if (new_index >= arr.length) {
var k = new_index - arr.length;
while ((k--) + 1) {
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr;
}
// enters infinite loop, never returns
const myCustomSort = array => {
while (findAnUnsortedElement(array) != 'SORTING_COMPLETE') {
const element = findAnUnsortedElement(array);
const index = array.findIndex(el => el == element);
console.log('moving', element);
array = move(array, index, index + 1);
console.log(array);
}
return array;
}
console.log(myCustomSort(unsorted));

const unsorted = ['c', 'd', 'a', 'b'];
const sorted = unsorted.sort();
It should work I'm not sure what's your issue.

The algorithm in the answer I gave earlier on, and which you (first) accepted, is really based on a heuristic.
For a sorted output to be guaranteed to not have any violations, you could treat this problem as a graph problem. Whenever two values can make a comparison that gives true (with either comparator function), then that pair represents an edge in the graph.
If the order is consistent, then there must be one value that is the least among the others, otherwise you would have a cycle.
So with that knowledge we can determine for each node in the graph how long the longest path is to such a least node. When you find the longest distance to such a least node, you can use the length of that path as an absolute order indication.
Here is an implementation:
class Node {
constructor(value) {
this.value = value;
this.prev = new Set;
this.order = 0; // No order yet
}
orderWith(other) {
if (other === this) return;
if (a_before_b(this.value, other.value) || b_before_a(other.value, this.value)) {
other.prev.add(this);
} else if (a_before_b(other.value, this.value) || b_before_a(this.value, other.value)) {
this.prev.add(other);
}
}
setOrder(path = new Set) {
// Use recursion to find length of longest path to "least" node.
if (this.order) return; // already done
if (path.has(this)) throw "cycle detected";
let order = 1;
for (let prev of this.prev) {
prev.setOrder(path.add(this));
order = Math.max(order, prev.order + 1);
}
this.order = order; // If order is 1, it is a "least" node
}
}
const a_before_b = (a, b) => {
if (a == 'a' && b == 'd') return true;
if (a == 'b' && b == 'c') return true;
}
const b_before_a = (a, b) => {
if (b == 'a' && a == 'c') return true;
if (b == 'b' && a == 'c') return true;
}
function mySort(arr) {
// Create a graph: first the nodes
let nodes = {}; // keyed by values in arr
for (let value of arr) nodes[value] = nodes[value] || new Node(value);
// Then the edges...
for (let i = 0; i < arr.length; i++) {
for (let j = i+1; j < arr.length; j++) {
nodes[arr[i]].orderWith(nodes[arr[j]]);
}
}
// Set absolute order, using the longest path from a node to a "least" node.
for (let node of Object.values(nodes)) node.setOrder();
// Sort array by order:
return arr.sort((a, b) => nodes[a].order - nodes[b].order);
}
const sorted = ['a', 'b', 'c', 'd'];
const unsorted = ['c', 'd', 'a', 'b'];
console.log(mySort(unsorted));

Maybe something like this
const sorted = ['a', 'b', 'c', 'd']; // I do NOT have access to this
const unsorted = ['c', 'd', 'a', 'b'];
const a_before_b = (a, b) => {
if (a == 'a' && b == 'd') return true;
if (a == 'b' && b == 'c') return true;
if (a == 'a' && b == 'c') return true;
}
const b_before_a = (a, b) => {
if (b == 'a' && a == 'c') return true;
if (b == 'b' && a == 'c') return true;
}
const mySortingFunction = (a, b) => {
if (a_before_b(a, b)) return -1;
if (b_before_a(a, b)) return 1;
return 0;
}
// doesn't produce correct sorting
console.log(unsorted.sort(mySortingFunction));

Related

Why are different instances of the same class overwriting each other?

So my problem lies in playAI and in miniMaxAlgorithm.
To start off, I make a copy of the instance myTicTacToe which I call tempgame afterwards I call miniMaxAlgorithm on that copy. All this to make sure myTicTacToe stays unchanged.
The problem though is myTicTacToe has afterwards the same values as tempgame.
I don't understand why.
/**************************TicTacToe Class**********************/
class TicTacToe {
constructor(playField = [
['E', 'E', 'E'],
['E', 'E', 'E'],
['E', 'E', 'E']
], human = 'X', computer = 'O', gameStatus = "playing", currentPlayer = 'X') {
this.playField = playField;
this.human = human;
this.computer = computer;
this.gameStatus = gameStatus;
this.bestMove;
this.startingPlayer = human;
this.currentPlayer = currentPlayer;
}
reset() {
this.playField = [
['E', 'E', 'E'],
['E', 'E', 'E'],
['E', 'E', 'E']
];
this.gameStatus = 'playing';
this.currentPlayer = this.startingPlayer;
$('#gamestate').text('');
}
checkGameState() {
/******************** Win conditions******************************/
if (this.currentPlayer === 'X' || this.currentPlayer === 'O') {
if (this.winOrNot()) {
$('#gamestate').text('Player ' + this.currentPlayer + ' won.');
this.gameStatus = 'over';
}
//********************** Check if it is a draw***************/
else {
/*which results in a draw*/
let arrayOfFreePlaces = this.freePositions();
if (!(arrayOfFreePlaces.length > 0)) {
$('#gamestate').text('It is a draw');
this.gameStatus = 'over';
}
}
}
}
/***********************/
winOrNot() {
if ((this.playField[0][0] === this.currentPlayer && this.playField[0][1] === this.currentPlayer && this.playField[0][2] === this.currentPlayer) ||
(this.playField[1][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[1][2] === this.currentPlayer) ||
(this.playField[2][0] === this.currentPlayer && this.playField[2][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
(this.playField[0][0] === this.currentPlayer && this.playField[1][0] === this.currentPlayer && this.playField[2][0] === this.currentPlayer) ||
(this.playField[0][1] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][1] === this.currentPlayer) ||
(this.playField[0][2] === this.currentPlayer && this.playField[1][2] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
(this.playField[0][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
(this.playField[0][2] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][0] === this.currentPlayer)
) {
return true;
} else {
return false;
}
}
freePositions() {
let emptyPositions = [];
for (let i = 0; i < this.playField.length; i++) {
for (let j = 0; j < this.playField[i].length; j++) {
if (this.playField[i][j] === 'E') {
emptyPositions.push([i, j]);
}
}
}
return emptyPositions;
}
// rate gamestate
rateField() {
// if computer wins +10
if (this.winOrNot(this.computer)) {
return 10;
}
// if human wins -10
else if (this.winOrNot(this.human)) {
return -10;
}
// if no one wins +0, aka drawm or not finished yet
else {
return 0;
}
}
}
//Prototypes of TicTacToe
TicTacToe.prototype.placeSign = function(row, column) {
// check if field is empty
if (this.playField[row][column] === 'E') {
if (this.currentPlayer === "X") {
this.playField[row][column] = 'X';
} else if (this.currentPlayer === "O") {
this.playField[row][column] = 'O';
}
this.checkGameState();
this.currentPlayer === this.computer ? this.currentPlayer = this.human : this.currentPlayer = this.computer;
} else {
console.log("Select an empty field!!");
}
};
/*******************Declarations*******************************/
let myTicTacToe = new TicTacToe();
let tempgame = new TicTacToe();
let bestMove;
/*****************Functions*************************/
// miniMaxAlgorithm
function miniMaxAlgorithm(TicTacToe1) {
/****************base case********************************/
// if the game is over , return rating
if (TicTacToe1.gameStatus === 'over') {
return TicTacToe1.rateField();
}
/******************************************/
//contains the rating of each move
let scores = [];
// containing the equivalent moves
let moves = [];
//fill the scores array
/**************************recursive case*******************************/
// create on array with containing all possible moves of the current tictactoe instance
let freeFields = TicTacToe1.freePositions();
for (let i = 0; i < freeFields.length; i++) {
//make a copy of the current tictactoe instance
let possibleTicTacToe = new TicTacToe(TicTacToe1.playField, TicTacToe1.human, TicTacToe1.computer, TicTacToe1.gameStatus, TicTacToe1.currentPlayer);
//play one of the possible moves
possibleTicTacToe.placeSign(freeFields[i][0], freeFields[i][1]);
// calling the function recursively until game is over
scores.push(miniMaxAlgorithm(possibleTicTacToe));
// adding place sign parameters ass an array inside moves
moves.push([freeFields[i][0], freeFields[i][1]]);
}
// Min Max Calculation
if (TicTacToe1.currentPlayer === TicTacToe1.computer) {
// search for the largest score and save its index in maxScoreIndex
let maxScoreIndex = 0;
for (let j = 1; j < scores.length; j++) {
if (scores[j] > scores[maxScoreIndex]) {
maxScoreIndex = j;
}
}
bestMove = moves[maxScoreIndex];
return scores[maxScoreIndex];
}
// tests best possible opponent moves (human)
else {
//
let minScoreIndex = 0;
for (let j = 1; j < scores.length; j++) {
if (scores[j] < scores[minScoreIndex]) {
minScoreIndex = j;
}
}
bestMove = moves[minScoreIndex];
return scores[minScoreIndex];
}
/**********************************************************************/
}
function updateFields() {
document.getElementById('field1').innerHTML = myTicTacToe.playField[0][0];
document.getElementById('field2').innerHTML = myTicTacToe.playField[0][1];
document.getElementById('field3').innerHTML = myTicTacToe.playField[0][2];
document.getElementById('field4').innerHTML = myTicTacToe.playField[1][0];
document.getElementById('field5').innerHTML = myTicTacToe.playField[1][1];
document.getElementById('field6').innerHTML = myTicTacToe.playField[1][2];
document.getElementById('field7').innerHTML = myTicTacToe.playField[2][0];
document.getElementById('field8').innerHTML = myTicTacToe.playField[2][1];
document.getElementById('field9').innerHTML = myTicTacToe.playField[2][2];
}
/**********************************************************/
//playAI
function playAI() {
//AI miniMaxEnd
tempgame = new TicTacToe (myTicTacToe.playField,myTicTacToe.human,myTicTacToe.computer,myTicTacToe.gameStatus,myTicTacToe.currentPlayer)
console.dir(myTicTacToe);
console.dir(tempgame);
miniMaxAlgorithm(tempgame);
console.dir(myTicTacToe);
console.dir(tempgame);
myTicTacToe.placeSign(bestMove[0],bestMove[1]);
//AI miniMaxEnd
updateFields();
}
I have an idea as to what may be causing this issue.
In javascript arrays (or lists) are passed by reference rather than value.
What this means that if you pass a function an array as a parameter it will pass a pointer to the list rather than a copy of the list.
In your TicTacToe class you have a variable called playField that is a list.
You create a clone of the TicTacToe object like this:
tempgame = new TicTacToe (myTicTacToe.playField, ... )
Here you are passing a reference to the existing playField list rather than cloning the list. i.e. Passing the address of the list rather than its contents.
Both your myTicTacToe and tempgame will use the same copy of the playField data. A change in one will cause a change in the other.
In order to create a new copy of the array it is standard to use the javascript slice operation:
tempgame = new TicTacToe (myTicTacToe.playField.slice(0), ... )
This operation will create a fresh copy of the array starting at the first element (0).
More information on using the slice operation to clone an array can be found here:
https://davidwalsh.name/javascript-clone-array

there is an string array A , and an string array B . I want to delete elements in A which are not in B

I think i messed somewhere, Here is my code.
var flag;
for (i = 0; i < A.length; i++)
{
flag = 0;
for (j = 0; j < B.length; j++)
{
if (A[i].indexOf(B[j]) != -1)
{
flag = 1;
}
}
if (flag == 0)
{
A.splice(i, 1);
}
}
It gives output not as per my need
Someone please Help me out
I would do the job like this;
//returns intersection of multiple arrays
Array.prototype.intersect = function(...a) {
return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
};
var a = [0,1,2,3,4,5],
b = [4,5,6,7,8,9];
a = a.intersect(b);
console.log(a);
You could use a function which generates first an object with all characters as properties and take it as hashtable for the filtering of array1.
function deleteSome(array1, array2) {
var o = Object.create(null);
array2.forEach(function (a) {
o[a] = true;
});
return array1.filter(function (a) {
return this[a];
}, o);
}
var a = 'abcdefgh'.split(''),
b = 'banana'.split('');
console.log(deleteSome(a,b));
Technically, array "a" should have only elements which are present in array "b".
var a = [1,2,3,4];
var b = [4,5,6];
var new_a = [];
a.map(function(v,i,a){
if(b.indexOf(v) !== -1){
new_a.push(v);
}
});
console.log(new_a); //[4]
By this way i can filter as many arrays as you want.
var a = ['A', 'A', 'R', 'S', 'M', 'D', 'E']
var b = ['C', 'X', 'D', 'F']
//you can add as many arrays as you want
/*var c = ['O', 'P', 'D', 'Q']
var d = ['R', 'D', 'D', 'Z']*/
var arrays = [a,b, /*c , d */];
var result = arrays.shift().filter(function(v) {
return arrays.every(function(a) {
return a.indexOf(v) !== -1;
});
});
console.log(JSON.stringify(result));

Alphabetical array sort - lowercase first

Given the array :
var myArray = ['d','a','k','v','z','A','t','G']
If I were to use :
myArray.sort()
The result would be :
myArray = ['A','G','a','d','k','t','v','z']
Is there a way of modifying the sort function or the output to put the uppercase letters at the end of the array like this :
myArray = ['a','d','k','t','v','z','A','G']
Either pure Javascript or jQuery would be OK.
Test the first character and apply compare:
var myArray = ['d', 'a', 'k', 'v', 'z', 'A', 't', 'G', 'V'];
myArray.sort(function (a, b) {
if (a[0] === a[0].toLocaleLowerCase() && b[0] === b[0].toLocaleLowerCase() ||
a[0] === a[0].toLocaleUpperCase() && b[0] === b[0].toLocaleUpperCase()) {
return a.localeCompare(b);
}
if (a[0] === a[0].toLocaleLowerCase()) {
return -1;
}
return 1;
});
document.write('<pre>' + JSON.stringify(myArray, 0, 4) + '</pre>');
Here's an example of the array sort using the compare function:
myArray.sort(function(a, b){
var test = a.charCodeAt(0) <= 90 && b.charCodeAt(0) <= 90;
if(test) return a.charCodeAt(0)-b.charCodeAt(0);
else if(a.charCodeAt(0) <= 90) return 1;
else if(b.charCodeAt(0) <= 90) return -1;
else return a.charCodeAt(0)-b.charCodeAt(0);
});
You should use custom compare function myArray.sort(compareFunction). Inside of the function you can use
conditions like if(a === a.toUpperCase()) and so on to create logic that you need.
var myArray = ['d','a','k','v','z','A','t','G'];
_.each(myArray, function(item){
if(item.charCodeAt(0) > 96){
smallArr.push(item)
}
else{
captArray.push(item)
}
});
smallArr.sort();
captArray.sort();
myArray = [];
myArray.push.apply(myArray, smallArr.concat(captArray));
once you have got this array
myArray = ['a','d','k','t','v','z','A','G']
do this to get it sorted by myArray = ['A','G','a','d','k','t','v','z']
var tempArray = myArray.join("").split(/(?=[A-Z])/);
myArray = tempArray.slice(1).concat( tempArray [0].split("") );

How to sort an array with null values

There are quite a few question related to the topic, but I couldn't find the right solution for my case.
var arr = [a, b, null, d, null]
and am sorting this Array with below logic
return function(a,b){
if(a === null){
return 1;
}
else if(b === null){
return -1;
}
else if(a === b){
return 0;
}
else if(ascending) {
return a < b ? -1 : 1;
}
else if(!ascending) {
return a < b ? 1 : -1;
}
};
I get the following outputs for
Ascending : [a, b, d, null,null]
Descending : [d, b, a, null,null]
Expected : [null, null,d, b, a]
What am I doing wrong?
function getSort (ascending) {
// if ascending, `null` will be pushed towards the end of the array by returning 1
var nullPosition = ascending ? 1 : -1
return function (a, b) {
// if a is null, push it towards whichever end null elements should end up
if (a == null) return nullPosition
// Note: at this point, a is non-null (previous if statement handled that case).
//
// If b is null, it must therefore be placed closer to whichever end the null
// elements should end up on. If ascending, null elements are pulled towards
// the right end of the array. If descending, null elements are pulled towards
// the left.
//
// Therefore, we return -nullPosition. If ascending, this is -1, meaning a comes
// before b; if descending, this is 1, meaning a comes after b. This is
// clearly the correct behavior, since ascending will push b, which is null,
// towards the end of the array (with -1) and descending will push b towards
// the beginning of the array.
if (b == null) return -nullPosition
// OTHERWISE, both elements are non-null, so sort normally.
// if a < b AND
// if ascending, a comes first, so return -1 == -nullPosition
// if descending, a comes after, so return -nullPosition == -(-1) == 1
if (a < b) return -nullPosition
// return the opposite of the previous condition
if (a > b) return nullPosition
// return 0 if both elements are equal
return 0
}
}
function write (arr) { arr.forEach(function (d) { document.write(d + "<br>")})}
var toSort = ['a', 'b', null, 'd', null]
var sortA = getSort(true)
var sortD = getSort(false)
document.write("<br>ASCENDING<br>")
write(toSort.sort(sortA))
document.write("<br>DESCENDING<br>")
write(toSort.sort(sortD))
You could use a two pass approach by checking null values first and then order by string.
To change the sort order, you could swap the parameters or use a negated result of the one of the function.
var data = ['a', 'b', null, 'd', null];
// ascending
data.sort(function (a, b) {
return (a === null) - (b === null) || ('' + a).localeCompare(b);
});
console.log(data);
// descending
data.sort(function (a, b) {
return (b === null) - (a === null) || ('' + b).localeCompare(a);
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
What about the below solution?
var arr = [null, 'e', 'a', 'b', null, 'd', null];
function sortBy(arr, ascending) {
return arr.sort((a, b) => {
if(!a) return ascending ? 1 : -1;
if(!b) return ascending ? -1 : 1;
if (ascending) return a > b ? 1 : -1;
return a > b ? -1 : 1;
})
}
const ascendingArr = sortBy(arr, true);
console.log(ascendingArr);
const decendingArr = sortBy(arr, false);
console.log(decendingArr);

How can I do a shallow comparison of the properties of two objects with Javascript or lodash?

Is there a way I can do a shallow comparison that will not go down and compare the contents of objects inside of objects in Javascript or lodash? Note that I did check lodash, but it appears to perform a deep comparison which I don't want to do.
var a = { x: 1, y: 2}
var b = { x: 1, y: 3}
Is there some way for example to compare a and b?
Simple ES6 approach:
const shallowCompare = (obj1, obj2) =>
Object.keys(obj1).length === Object.keys(obj2).length &&
Object.keys(obj1).every(key => obj1[key] === obj2[key]);
Here I added the object keys amount equality checking for the following comparison should fail (an important case that usually does not taken into the account):
shallowCompare({ x: 1, y: 3}, { x: 1, y: 3, a: 1}); // false
2019 Update. Per Andrew Rasmussen' comment we also need to take into account undefined case. The problem with the previous approach is that the following comparison returns true:
({ foo: undefined })['foo'] === ({ bar: undefined })['foo'] // true
So, explicit keys existence check is needed. And it could be done with hasOwnProperty:
const shallowCompare = (obj1, obj2) =>
Object.keys(obj1).length === Object.keys(obj2).length &&
Object.keys(obj1).every(key =>
obj2.hasOwnProperty(key) && obj1[key] === obj2[key]
);
function areEqualShallow(a, b) {
for(var key in a) {
if(!(key in b) || a[key] !== b[key]) {
return false;
}
}
for(var key in b) {
if(!(key in a) || a[key] !== b[key]) {
return false;
}
}
return true;
}
Notes:
Since this is shallow, areEqualShallow({a:{}}, {a:{}}) is false.
areEqualShallow({a:undefined}, {}) is false.
This includes any properties from the prototype.
This uses === comparison. I assume that is what you want. NaN === NaN is one case that may yield unexpected results. If === is not what you want, substitute with the comparison you want.
EDIT: If the same keys are in each object, then
function areEqualShallow(a, b) {
for(var key in a) {
if(a[key] !== b[key]) {
return false;
}
}
return true;
}
Paul Draper's solution can be optimized by removing the compare in the second pass.
function areEqualShallow(a, b) {
for (let key in a) {
if (!(key in b) || a[key] !== b[key]) {
return false;
}
}
for (let key in b) {
if (!(key in a)) {
return false;
}
}
return true;
}
keeping in mind that it only for shallow and only for strings and numbers
function equals(obj1, obj2) {
return Object.keys(obj1)
.concat(Object.keys(obj2))
.every(key => {
return obj1[key] === obj2[key];
});
}
This is lifted from fbjs:
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* #typechecks
*
*/
/*eslint-disable no-self-compare */
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
}
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA, objB) {
if (is(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (var i = 0; i < keysA.length; i++) {
if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
module.exports = shallowEqual;
I recommend copying it into your own project if you need to use it, as their README clearly states that they may remove or modify this and any other code in the lib without warning.
To do a "shallow" comparison where inherited properties should be ignored and NaN should equal NaN, the following should do the job. It checks that each object has the same own properties and that the values are === or both NaN:
function checkProperties(a, b) {
var equal = true;
// For each property of a
for (var p in a) {
// Check that it's an own property
if (a.hasOwnProperty(p)) {
// Check that b has a same named own property and that the values
// are === or both are NaN
if (!b.hasOwnProperty(p) ||
(b[p] !== a[p] && !(typeof b[p] == 'number' && typeof a[p] == 'number' && isNaN(b[p] && isNaN(a[p]))))) {
// If not, set equal to false
equal = false;
}
}
// If equal is false, stop processing properties
if (!equal) break;
}
return equal;
}
Using recent features like Object.keys to get own properties, then
function checkProperties(a, b) {
return Object.keys(a).every(function(p) {
return b.hasOwnProperty(p) &&
(b[p] == a[p] || (typeof a[p] == 'number' && typeof b[p] == 'number' && isNaN(b[p]) && isNaN(a[p])));
});
}
// Compare a to b and b to a
function areEqualShallow(a, b) {
return checkProperties(a, b) && checkProperties(b, a);
}
// Minimal testing
var a = {foo:'a', bar:2};
var b = {foo:'a', bar:2};
var c = {foo:'c', bar:2};
var d = {foo:'a', bar:2, fum:0};
console.log('a equal to b? ' + areEqualShallow(a,b)); // true
console.log('a equal to c? ' + areEqualShallow(a,c)); // false
console.log('a equal to d? ' + areEqualShallow(a,d)); // false
With newer features, the checkProperties function can be simplified somewhat:
const shallowEq = (a, b) =>
[...Object.keys(a), ...Object.keys(b)].every((k) => b[k] === a[k]);
If you really need to check undefined values, then this extension should satisfy #AndrewRasmussen:
const shallowEq2 = (a, b) =>
[...Object.keys(a), ...Object.keys(b)].every(k => b[k] === a[k] && a.hasOwnProperty(k) && b.hasOwnProperty(k));
In most use cases you don't really need all the checks, and you only want to see if b contains everything a contains. Then an a-centric check would be really really terse:
const shallowEq3 = (a, b) => Object.keys(a).every(k => b[k] === a[k]);
var a = { x: 1, y: 2}
var b = { x: 1, y: 3}
function shalComp (obj1, obj2) {
var verdict = true;
for (var key in obj1) {
if (obj2[key] != obj1[key]) {
verdict = false;
}
}
return verdict;
}
const isEqual = (a, b) => {
// compare keys
const xKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (xKeys.length !== bKeys.length) {
return false;
}
// compare values
for (let objKeys in xKeys) {
if (xKeys[objKeys !== bKeys[objKeys]]) {
return false;
}
}
return true;
};
var a = {
x: 1,
y: 2,
};
var b = {
x: 1,
y: 2,
};
console.log(isEqual(a, b)); // true
And you can see this video is very helpful for your question : JS Tutorial: Find if Two Object Values are Equal to Each Other

Categories

Resources