JavaScript Variables Not Defining Correctly Inside Function - javascript

I'm new to JS and am trying to create a simple 'swap array elements if array A element is bigger than array B element' function. In the swapIndexes function, I don't understand why I can't define the variables as shown in the comments. For example, it works if it state arrA[c] rather than let a = arrA[c].
Why does this happen? Can anyone give some beginner tips on how best to go about something like this? My code feels verbose. Thanks for any help here.
var arrA = [0, 1, 2, 7, 6],
arrB = [0, 1, 2, 5, 7],
indexesToSwap = [],
aValuesToSwap = [],
bValuesToSwap = [],
needSwapping = false;
arrA.forEach(getSwappableIndexesAndValues);
indexesToSwap.forEach(swapIndexes);
function getSwappableIndexesAndValues(c, i) {
let b = arrB[i];
if (c > b) {
needSwapping = true;
indexesToSwap.push(i);
aValuesToSwap.push(b);
bValuesToSwap.push(c);
}
}
function swapIndexes(c, i) {
//let a = arrA[c]; fails why???
//let b = arrB[c]; fails why???
//a = aValuesToSwap[i]; fails why???
//b = bValuesToSwap[i]; fails why???
arrA[c] = aValuesToSwap[i];
arrB[c] = bValuesToSwap[i];
}
console.log(arrA);
console.log(arrB);

In javascript, when you create a variable from a given index in an array, This will create a new memory space containing a copy of the value at this index. The newly created variable will not point to the content of the array and thus, modifying this variable will not modify the content of the array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

indexesToSwap has all the information you need to swap. The swap value arrays (aValuesToSwap, bValuesToSwap) are greatly complicating matters and are wholly unnecessary.
Regardless of the values to swap arrays, swapping is a fundamental operation and typically involves a simple temporary, e.g.
temp = arrA[i];
arrA[i] = arrB[i];
arrB[i] = temp;
Discarding the complexities, here's an alternative to the function getSwappableIndexesAndValues
function getSwappableIndexes(c, i) {
if (c > arrB[i])
indexesToSwap.push(i);
}
And a simplified swap function
function swapIndexes(c, i) {
let temp = arrA[c];
arrA[c] = arrB[c];
arrB[c] = temp;
}
I have to say further though that the use of Array.forEach wildly complicates the entire solution. Unless this is an assignment, using a simple for-loop is best here.
// swaps values between arrays where the value in
// array a is greater than the value in array b
//
function swapIfGreaterThan(a,b) {
for(let i = 0; i < a.length && i < b.length; i++) {
if(a[i] > b[i]) {
let temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
}

var arrA = [0, 1, 2, 7, 6],
arrB = [0, 1, 2, 5, 7],
indexesToSwap = [],
aValuesToSwap = [],
bValuesToSwap = [],
needSwapping = false;
arrA.forEach(getSwappableIndexesAndValues);
indexesToSwap.forEach(swapIndexes);
function getSwappableIndexesAndValues(c, i) {
let b = arrB[i];
if (c > b) {
needSwapping = true;
indexesToSwap.push(i);
aValuesToSwap.push(b);
bValuesToSwap.push(c);
}
}
function swapIndexes(c, i) {
//let a = arrA[c]; fails why???
//let b = arrB[c]; fails why???
//a = aValuesToSwap[i]; fails why???
//b = bValuesToSwap[i]; fails why???
arrA[c] = bValuesToSwap[i];
arrB[c] =aValuesToSwap[i];
console.log( arrA[c], arrB[c]);
console.log( aValuesToSwap[i], bValuesToSwap[i]);
}
console.log(arrA);
console.log(arrB);
It is not possible array values and primitive data type values are different. If you try with the array of the object your attempt will be correct.

Related

Remove duplicates from an array within an array [duplicate]

Input:
[[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]]
The output I want:
[[-1,-1,2],[-1,0,1]]
Any other ideas except this one?
Thanks
You won't really get around stringifying the arrays, as that's the simplest (and reasonably fast) way to compare them by value. So I'd go for
Array.from(new Set(input.map(JSON.stringify)), JSON.parse)
See also Remove Duplicates from JavaScript Array for other approaches, though most of them will require two values to be comparable by ===.
Magic
d.filter(( t={}, a=> !(t[a]=a in t) ));
I assume your input data are in array d. Explanation here.
let d = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]];
var r = d.filter((t={},a=>!(t[a]=a in t)));
console.log(JSON.stringify(r));
There's already a good utility for that, try Lodash, one of the function of it is _.uniqWith, with that function you can do the following.
<script src="/path/to/lodash.js"></script>
<script>
var aa = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]];
console.log(aa);
console.log(_.uniqWith(aa,_.isEqual));
</script>
You can create a hashMap and save values in it. This will always hold last value.
var data = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]]
var hashMap = {}
data.forEach(function(arr){
// If your subArrays can be in any order, you can use .sort to have consistant order
hashMap[arr.join("|")] = arr;
});
var result = Object.keys(hashMap).map(function(k){
return hashMap[k]
})
console.log(result)
jsfiddle
Borrowing the array comparison code from this post
// Warn if overriding existing method
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
var old = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]], n = [];
while(old.length) {
var arr = old.shift(), matched = false;
for(var i = 0, len = n.length; i < len; i++) {
if (arr.equals(n[i])) {
matched = true;
break;
}
}
if (!matched) {
n.push(arr);
}
}
const removeDuplicates = (arr = []) => {
const map = new Map();
arr.forEach((x) => map.set(JSON.stringify(x), x));
arr = [...map.values()];
return arr;
};
console.log(
removeDuplicates([
[1, 1, 6],
[1, 2, 5],
[1, 7],
[1, 2, 5],
[1, 7],
[2, 6],
])
);
// we can use simple JS object also to store unique elements like { "[1, 1, 6]" : [1, 1, 6] }
//resource - https://hackinbits.com/articles/how-to-iterate-a-map-in-javascript---map-part-2

JavaScript - input array changed without push statement

I was on leet code #283
Given an array nums, write a function to move all 0's to the end of it
while maintaining the relative order of the non-zero elements.
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
Can somebody please explain the following code.
var moveZeroes = function(nums) { // nums is [0,1,0,3,12]
var len = nums.length;
for (let lastNonZero = 0, cur = 0; cur < len; cur++) {
if (nums[cur] !== 0) {
[nums[lastNonZero], nums[cur]] = [nums[cur], nums[lastNonZero]]; // what exactly happened here
lastNonZero++;
}
}
return nums;
};
How did the for loop work and how is the nums array rearranged?
The line: [nums[lastNonZero], nums[cur]] = [nums[cur], nums[lastNonZero]] is just replacement. If you have [1, 2] and use this code, you'll end up with [2, 1]. The function you provided will run through the loop and move 0 to the right and the next number to the left, until it gets to [0, 0].
I believe The following
[nums[lastNonZero], nums[cur]] = [nums[cur], nums[lastNonZero]];
is short for
nums[lastNonZero] = nums[cur];
// and
nums[cur] = nums[lastNonZero];
but simultaneously with out doing
const tempCurrent = nums[cur];
const tempLastNonZero = nums[lastNonZero];
nums[lastNonZero] = tempCurrent;
nums[cur] = tempLastNonZero;
Edit:
destructuring is the name of this syntax thanks to #SanthoshN
destructuring assignment from #gaetanoM and #Jacque Goupil is what that is
[nums[lastNonZero], nums[cur]] = [nums[cur], nums[lastNonZero]];
This is a es6 syntax called de de-structuring, whats happening there is a just a swap.
example of object destructuring
obj = { 'a': 1, 'b': 2}
const { a, b } = obj;
instead of accessing obj.a everywhere you can now just use a, which is destructed as local variable.
similarly lets consider an array
arr = [1,2,3];
const [a,b,c] = arr;
console.log(a) will result in 1;
Essentially, the first element in the array is assigned to the first variable int the array.
Hope it clarifies.
[nums[lastNonZero], nums[cur]] = [nums[cur], nums[lastNonZero]] just do swapping.
let [a,b] = ['b','a']
console.log(a,b)
Alternate method is to use filter and concat.
Here idea is
By filter we take all non zero element in a variable.
Add number of zero equal to length of original arr - filtered arr
let arr = [1,2,3,0,15,10,82,19,0,5,8,7]
let op = arr.filter(e=>e)
let final = op.concat(new Array(arr.length - op.length).fill(0))
console.log(final)

javascript: how to remove duplicate arrays inside array of arrays

Input:
[[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]]
The output I want:
[[-1,-1,2],[-1,0,1]]
Any other ideas except this one?
Thanks
You won't really get around stringifying the arrays, as that's the simplest (and reasonably fast) way to compare them by value. So I'd go for
Array.from(new Set(input.map(JSON.stringify)), JSON.parse)
See also Remove Duplicates from JavaScript Array for other approaches, though most of them will require two values to be comparable by ===.
Magic
d.filter(( t={}, a=> !(t[a]=a in t) ));
I assume your input data are in array d. Explanation here.
let d = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]];
var r = d.filter((t={},a=>!(t[a]=a in t)));
console.log(JSON.stringify(r));
There's already a good utility for that, try Lodash, one of the function of it is _.uniqWith, with that function you can do the following.
<script src="/path/to/lodash.js"></script>
<script>
var aa = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]];
console.log(aa);
console.log(_.uniqWith(aa,_.isEqual));
</script>
You can create a hashMap and save values in it. This will always hold last value.
var data = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]]
var hashMap = {}
data.forEach(function(arr){
// If your subArrays can be in any order, you can use .sort to have consistant order
hashMap[arr.join("|")] = arr;
});
var result = Object.keys(hashMap).map(function(k){
return hashMap[k]
})
console.log(result)
jsfiddle
Borrowing the array comparison code from this post
// Warn if overriding existing method
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
var old = [[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,-1,2],[-1,0,1],[-1,0,1]], n = [];
while(old.length) {
var arr = old.shift(), matched = false;
for(var i = 0, len = n.length; i < len; i++) {
if (arr.equals(n[i])) {
matched = true;
break;
}
}
if (!matched) {
n.push(arr);
}
}
const removeDuplicates = (arr = []) => {
const map = new Map();
arr.forEach((x) => map.set(JSON.stringify(x), x));
arr = [...map.values()];
return arr;
};
console.log(
removeDuplicates([
[1, 1, 6],
[1, 2, 5],
[1, 7],
[1, 2, 5],
[1, 7],
[2, 6],
])
);
// we can use simple JS object also to store unique elements like { "[1, 1, 6]" : [1, 1, 6] }
//resource - https://hackinbits.com/articles/how-to-iterate-a-map-in-javascript---map-part-2

Find missing element by comparing 2 arrays in Javascript

For some reason I'm having some serious difficulty wrapping my mind around this problem. I need this JS function that accepts 2 arrays, compares the 2, and then returns a string of the missing element. E.g. Find the element that is missing in the currentArray that was there in the previous array.
function findDeselectedItem(CurrentArray, PreviousArray){
var CurrentArrSize = CurrentArray.length;
var PrevousArrSize = PreviousArray.length;
// Then my brain gives up on me...
// I assume you have to use for-loops, but how do you compare them??
return missingElement;
}
Thank in advance! I'm not asking for code, but even just a push in the right direction or a hint might help...
Problem statement:
Find the element that is missing in the currentArray that was there in the previous array.
previousArray.filter(function(x) { // return elements in previousArray matching...
return !currentArray.includes(x); // "this element doesn't exist in currentArray"
})
(This is as bad as writing two nested for-loops, i.e. O(N2) time*). This can be made more efficient if necessary, by creating a temporary object out of currentArray, and using it as a hashtable for O(1) queries. For example:)
var inCurrent={}; currentArray.forEach(function(x){ inCurrent[x]=true });
So then we have a temporary lookup table, e.g.
previousArray = [1,2,3]
currentArray = [2,3];
inCurrent == {2:true, 3:true};
Then the function doesn't need to repeatedly search the currentArray every time which would be an O(N) substep; it can instantly check whether it's in currentArray in O(1) time. Since .filter is called N times, this results in an O(N) rather than O(N2) total time:
previousArray.filter(function(x) {
return !inCurrent[x]
})
Alternatively, here it is for-loop style:
var inCurrent = {};
var removedElements = []
for(let x of currentArray)
inCurrent[x] = true;
for(let x of previousArray)
if(!inCurrent[x])
removedElements.push(x)
//break; // alternatively just break if exactly one missing element
console.log(`the missing elements are ${removedElements}`)
Or just use modern data structures, which make the code much more obvious:
var currentSet = new Set(currentArray);
return previousArray.filter(x => !currentSet.has(x))
*(sidenote: or technically, as I illustrate here in the more general case where >1 element is deselected, O(M*N) time)
This should work. You should also consider the case where the elements of the arrays are actually arrays too. The indexOf might not work as expected then.
function findDeselectedItem(CurrentArray, PreviousArray) {
var CurrentArrSize = CurrentArray.length;
var PreviousArrSize = PreviousArray.length;
// loop through previous array
for(var j = 0; j < PreviousArrSize; j++) {
// look for same thing in new array
if (CurrentArray.indexOf(PreviousArray[j]) == -1)
return PreviousArray[j];
}
return null;
}
Take a look at underscore difference function: http://documentcloud.github.com/underscore/#difference
I know this is code but try to see the difference examples to understand the way:
var current = [1, 2, 3, 4],
prev = [1, 2, 4],
isMatch = false,
missing = null;
var i = 0, y = 0,
lenC = current.length,
lenP = prev.length;
for ( ; i < lenC; i++ ) {
isMatch = false;
for ( y = 0; y < lenP; y++ ) {
if (current[i] == prev[y]) isMatch = true;
}
if ( !isMatch ) missing = current[i]; // Current[i] isn't in prev
}
alert(missing);
Or using ECMAScript 5 indexOf:
var current = [1, 2, 3, 4],
prev = [1, 2, 4],
missing = null;
var i = 0,
lenC = current.length;
for ( ; i < lenC; i++ ) {
if ( prev.indexOf(current[i]) == -1 ) missing = current[i]; // Current[i] isn't in prev
}
alert(missing);
And with while
var current = [1, 2, 3, 4],
prev = [1, 2, 4],
missing = null,
i = current.length;
while(i) {
missing = ( ~prev.indexOf(current[--i]) ) ? missing : current[i];
}
alert(missing);
This is my approach(works for duplicate entries too):-
//here 2nd argument is actually the current array
function(previousArray, currentArray) {
var hashtable=[];
//store occurances of elements in 2nd array in hashtable
for(var i in currentArray){
if(hashtable[currentArray[i]]){
hashtable[currentArray[i]]+=1; //add 1 for duplicate letters
}else{
hashtable[currentArray[i]]=1; //if not present in hashtable assign 1
}
}
for(var i in previousArray){
if(hashtable[previousArray[i]]===0 || hashtable[previousArray[i]] === undefined){ //if entry is 0 or undefined(means element not present)
return previousArray[i]; //returning the missing element
}
else{
hashtable[previousArray[i]]-=1; //reduce count by 1
}
}
}
Logic is that i have created a blank array called hashtable. We iterate currentArray first and use the elements as index and values as counts starting from 1(this helps in situations when there are duplicate entries). Then we iterate through previousArray and look for indexes, if they match we reduce the value count by 1. If an element of 2nd array doesnt exist at all then our undefined check condition fires and we return it. If duplicates exists, they are decremented by 1 each time and when 0 is encountered, that elment is returned as missing element.

How to convert a var in an array after it is created?

Working on my first JS app, BlackJack, and have been stuck at this point for a while: what I'm trying to do is if var a is called from the array it could = 11 or 1 depending on the total value. Is there no way to change the value of an item in an array after you set up said array?
var j = 10;
var q = 10;
var k = 10;
var a;
var totalPlayer = 12;
var cards = [2, 2, ..., a, a, a];
function processIt() {
playerCard1 = cards[48]; //this is calling for var a form the array
if (totalPlayer > 11) {
a = 1;
} else {
a = 11;
}
var cpu1 = document.getElementById("cpu1");
cpu1.innerHTML = playerCard1; //this is calling for var a form the array
}
I have also tried converting it to a sting then back to a var, failed.
If I'm reading correctly, you've set up your array
var cards = [2, 2, ..., a, a, a];
and now you want to change all those a's?
Unfortunately (since a is a primitive) you'll have to manually change the values in your array that currently equal a, and set them to the updated value.
for (var i = 0, max = cards.length; i < max; i++)
if(cards[i] === a)
cards[i] = newValue;
EDIT
As hop points out, just be aware that if a is equal to 2, then all indexes in your array equal to 2 will be replaced—those first few indexes that you manually set to 2, and also those indexes at the end that you set to a. But since you say that a will either be 1 or 11, it looks like you've set things up in such a way that this won't be an issue.
You cannot do what you are expecting this way.
var a = 1;
var cards = [2, 2, a, a, a];
a = 5;
alert(cards); // This should print 2,2,5,5,5 is what you expect.
// But, it will only print 2,2,1,1,1
You can store all the indexes for which you set the value as 'a'. After all, you are constructing the array and it should be no hurdle for you.
In our case, you will have another array
var aIndexes = [2,3,4];
Then you can change the value of cards array like below.
if (totalPlayer > 11) {
a = 1;
} else {
a = 11;
}
for(var i =0; i< aIndexes.length; i++){
cards[i] = a;
}
The following line:
var cards = [2, 2, ..., a, a, a];
...copies the value of a into the last three array positions. Those values have no relationship with a itself. Changing a later has no effect on the array. It must be updated manually:
if (totalPlayer > 11) {
a = 1;
} else {
a = 11;
}
cards.splice(-3, 3, a, a, a);
var a;
…
var cards = [2, 2, ..., a, a, a];
puts the value of a at the time of creation of the Array instance as element of the array, not some sort of pointer to a. It is equivalent here to
var cards = [2, 2, ..., undefined, undefined, undefined];
You can either modify the array directly, as others have pointed out, or store references to an Object instance in the array (references are values). Modifying a property of that Object instance would modify it for all references to the Object instance then.
var a = {
toString: function () { return String(this.valueOf()); },
valueOf: function () { return this.value; }
};
// …
var cards = [2, 2, ..., a, a, a];
function processIt()
{
var playerCard1 = cards[48];
if (totalPlayer > 11)
{
a.value = 1;
}
else
{
a.value = 11;
}
var cpu1 = document.getElementById("cpu1");
cpu1.innerHTML = playerCard1;
}
Accessing a in string or numeric expression context would then yield the value of the value property of the Object instance referred to by a:
/* y + a.valueOf() */
var x = y + a;
/* z + a.toString() */
var y = z + String(a);
You must decide whether either approach makes sense in your case, as I do not know Blackjack well, and your question is rather confused.
BTW, you have forgotten to declare playerCard1 which causes it to leak into outer execution contexts or causes a runtime error (ES 5.x strict mode). I have fixed it for you. In general, you want to reduce the number of global variables to the absolute necessary minimum.

Categories