how to pass a shallow copy of an array into a function - javascript

I want to get a different slice of the array in the console.log depending on what button I press, however no matter what button I end up pressing I always get the last 20 elements of the array.
How can I make it behave as expected?
for (var i = 0; i < array.length; i++) {
var b;
var NewArr = [];
if (i % 20 == 0) {
NewArr = array.slice(i, i + 20);
b = createButton(i + "-" + (i + 20), NewArr);
b.position(x, y + i * 1.5);
b.mousePressed(function () {
console.log(NewArr);
});
}
}

Use let instead of var.
var declarations are hoisted (there are many other answers/wiki articles about JS hoisting already if you want to read more) to the top of the current function scope, even you declare it inside the for loop.

Related

multidimensional array indexOf not working js

I'm trying to find an index of a number in a 2d array, but console gives out
Uncaught TypeError: block[((a * 10) + c)].indexOf is not a function
I think it has something to do with the way of accessing the array element, but can't seem to find the problem.
Here's the code.
var block = [];
var temp;
var del;
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
temp = parseInt(prompt("enter element number " + b + " of row number " + a));
console.log(temp);
if(temp>0){
block[a*10+b] = temp;
}else{
block[a*10+b] = [1,2,3,4,5,6,7,8,9];
}
// console.log(block[a*10+b]);
}
}
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
if(typeof(block[a][b]) == "number"){
for(var c = 0;c < 9;c++){
if(c != b){
del = block[a*10+c].indexOf(b);
block[a*10+c].splice(del,1);
}
}
}
}
}
You have a mix of data types assigned to the block array. When the user enters a value that is not numeric, you assign indeed a nested array to one of the block elements, but not so when the user enters a valid number.
From what I think you are doing (a Sudoko game?) this might be intended: the numbers are known values in the grid, the nested arrays represent a list of values that are still possible at that particular cell.
But then in the second part of your code, you should check in which of the two cases you are, as you only want to remove array elements if the value you are looking at is indeed an array. This test you can do with Array.isArray().
There are also some other issues in the second part of your script:
The expression block[a][b] is not consistent with how you have filled that array: it should be block[a*10+b] to be consistent.
the b in .indexOf(b) is wrong: you are not looking for that value, but for block[a*10+b].
the splice() is always executed, even if the indexOf returned -1. This leads to an undesired effect, because if the first argument to splice() is negative, the index really is counted from the end of the array, and still an element is removed from the array. This should not happen: you should only execute the splice if the indexOf result is non-negative.
Below I have put a working version, but in order to avoid the almost endless prompts, I have provided this snippet with a textarea where you can input the complete 9x9 grid in one go, and then press a button to start the execution of your code:
document.querySelector('button').onclick = function () {
var block = [];
var temp;
var del;
var text = document.querySelector('textarea').value.replace(/\s+/g, '');
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
temp = parseInt(text[a*9+b]); // <-- get char from text area
if(temp>0){
block[a*10+b] = temp;
}else{
block[a*10+b] = [1,2,3,4,5,6,7,8,9];
}
}
}
for(var a = 0;a < 9;a++){
for(var b = 0;b < 9;b++){
var num = block[a*10+b]; // <-- get content, fix the index issue
if(typeof num == "number"){
for(var c = 0;c < 9;c++){
if(c != b && Array.isArray(block[a*10+c])){ //<-- add array-test
del = block[a*10+c].indexOf(num); // <-- not b, but num
if (del > -1) // <-- only splice when found
block[a*10+c].splice(del,1);
}
}
}
}
}
document.querySelector('pre').textContent = 'block='+ JSON.stringify(block);
};
<textarea rows=9>
53..7....
6..195...
.98....6.
8...6...3
4..8.3..1
7...2...6
.6....28.
...419..5
....8..79
</textarea>
<button>Process</button>
<pre></pre>
Note that there are elements in block which remain null. I suppose you intended this: as you multiply a with 10, and only store 9 values per "row", there is always one index that remains untouched.
I haven't looked over your second for loop, but you can try applying similar logic there as in the snippet I've provided. The issue is that you need to create a temporary array inside the outer for loop over values of a (but NOT inside the inner, nested for loop over values of b). Inside the for loop for values of b, then, you need to push something into that temporary array (which I called temp). Then, outside of the b for loop, but before the next iteration of a, push that temporary array temp to the block array. In this way, you will generate a 2D array.
var block = [];
var del;
for(var a = 0; a < 9; a++) {
let temp = [];
for(var b = 0; b < 9; b++) {
let num = parseInt(prompt(`Enter element ${b} of row ${a}:`));
if (num > 0) {
temp.push(num);
} else {
// block[a*10+b] = [1,2,3,4,5,6,7,8,9];
temp.push(b);
}
}
block.push(temp);
}

Using hash tables in Javascript: is an array of arrays adequate?

I need an hash table in Javascript, i.e. to implement an associative array which maps keys (strings) to values (in my case, these are several integer arrays). I realized that this kind of approach is not commonly used, or at least I haven't found it on the web yet:
var hash = ['s0'];
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
hash['s'+i] = [r, r*2, r^2];
}
console.log(hash);
hash.forEach(function (v, i, a) {
document.getElementById('foreach').innerHTML += i + ' => ' + v + '<br>';
})
for (var i = 5; i >= 1; i--) {
var key = 's'+i;
document.getElementById('for').innerHTML += key + ' => [' + hash[key].toString() + ']<br>';
}
<p id="foreach">forEach (val,index):<br/></p>
<p id="for">for:<br/></p>
Perhaps due to the fact that the declared array seems to not be correctly mapped after I add the new values (open your console and click the + button, you can see the values are there even when it displays [s1]). The forEach keeps assuming the array only has one value, but if I access any of those keys directly, e.g. hash['s3'], the respective array is returned.
Therefore, am I doing something wrong? Should I even use this approach?
If objects in JSON are more appropriate for this case, what is the best way to implement something simple and similar to the example above?
Furthermore, if key_string is the string I want as key, hash.push(key_string: val_array) fails because it is not a "formal parameter". However by doing something like:
hash.push({'key':key_string,'value':val_array})
How can I access one of those arrays in the simplest way possible through its associated key?
Why can't you use a JavaScript Map()?
MDN JavaScript Reference: Map
I modified your code below to use a Map instead of an Array:
var map = new Map();
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
map.set('s'+i, [r, r*2, r^2]);
}
console.log(map);
map.forEach(function (v, i, m) {
document.getElementById('foreach').innerHTML += i + ' => ' + v + '<br>';
})
for (var i = 5; i >= 1; i--) {
var key = 's'+i;
document.getElementById('for').innerHTML += key + ' => [' + map.get(key).toString() + ']<br>';
}
<p id="foreach">forEach (val,index):<br/></p>
<p id="for">for:<br/></p>
Javascript's object type covers all the behavior you are looking for:
var obj = {};
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
obj['s'+i] = [r, r*2, r^2];
}
The cool thing about the object type in Javascript is you can access properties using the associate array-like syntax or using dot notation.
obj['key'] === obj.key
Please, check this example in the console.
var hash = {'s0':' '};
for (var i = 5; i >= 1; i--) {
var r = Math.floor(Math.random() * 3);
hash['s'+i] = [r, r*2, r^2];
}
see that hash object now contains key-value mapping
console.log(hash);
to access the object with forEach you can extract the keys of the object as an array and iterate over it:
Object.keys(hash).forEach(function (v, i, a) {
console.log( i , v, hash[v] );
})
You may also start using such libraries as https://lodash.com/ that implement a number of common operations over collections.

js Bubble sort not processing all elements correctly

I pass in elements array [5,4,3]. The bubble sort manages to push 5 the end, this is fine, but when it loops through a third time to locate the second to last element, the loop breaks and the incorrect ordered array is returned... not sure why, cheers guys
this.bubbleSort = function (elements) {
//Loop through to the second to last index. By the time we get to the last index, its already //been compared with what’s in front of it
var hasHadChange;
for (var x = 0; x < elements.length - 1; x++) {
hasHadChange = false;
//Loop through to the second to last index.
for (var y = 0; y < elements.length - 1; y++) {
//Check the current item(x) in the array plus the item next to current item (x+1), if its larger
if (elements[y] > elements[y + 1]) {
//Acknowledge there has been a change
hasHadChange = true;
//Swap the items around
var temp = elements[y];
elements[y] = elements[y + 1];
elements[y + 1] = temp;
//This continues until the largest value has bubbled to the right
}
}
}
return elements;
}
You need to use separate variables for the inner and the outer loop. When you exit the inner loop, x will be equal to the length, so the outer loop will end also.
You should use separate variables in inner and outer loops. Using y in inner loop instead will give you the correct answer.
var bubbleSort = function (elements) {
//Loop through to the second to last index. By the time we get to the last index, its already //been compared with what’s in front of it
var hasHadChange;
for (var x = 0; x < elements.length - 1; x++) {
hasHadChange = false;
//Loop through to the second to last index.
for (y = 0; y < elements.length - 1; y++) {
//Check the current item(x) in the array plus the item next to current item (x+1), if its larger
if (elements[y] > elements[y + 1]) {
//Acknowledge there has been a change
hasHadChange = true;
//Swap the items around
var temp = elements[y];
elements[y] = elements[y + 1];
elements[y + 1] = temp;
//This continues until the largest value has bubbled to the right
}
}
}
return elements;
}
The reason behind this is variable hoisting.
When a variable is declared, it breaks into two parts. One part moves to top of it's scope, other stays at it's position. For Example,
function foo() {
if (false) {
var x = 1;
}
var y = 1;
}
Will look like :
function foo() {
var x = undefined; //Declaration of x is hoisted
var y = undefined; //Declaration of y is hoisted
if (false) {
x = 1; //Assignment still occurs where we intended
}
y = 1; //Assignment still occurs where we intended
}
This is what happened in the code. Using same variable in both loops makes them overwrite each other values. Hence the result.
From ECMAScript standard 5.1 :
A variable statement declares variables that are created as defined in 10.5. Variables are initialised to undefined when created. A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.
See this MDN doc for more details. Look for topic var hoisting.
Update
Using let which has block level scope, you can have variable x in both loops.
var bubbleSort = function (elements) {
//Loop through to the second to last index. By the time we get to the last index, its already //been compared with what’s in front of it
var hasHadChange;
for (var x = 0; x < elements.length - 1; x++) {
hasHadChange = false;
//Loop through to the second to last index.
for (let x = 0; x < elements.length - 1; x++) {
//Check the current item(x) in the array plus the item next to current item (x+1), if its larger
if (elements[x] > elements[x + 1]) {
//Acknowledge there has been a change
hasHadChange = true;
//Swap the items around
var temp = elements[x];
elements[x] = elements[x + 1];
elements[x + 1] = temp;
//This continues until the largest value has bubbled to the right
}
}
}
return elements;
}
You are using the same control variable x for both for-cycles, should use different like x and y for example.

callback function within a loop

I am really struggling with concept of scope in my code.
I am simply trying to create a 'callback' function which will add a className to a variable. As it's inside a function, I am passing the global variable as parameters to the callback function using the concept of closure (still dont understand how closure works).
var ePressCuttingsArray = $(".cPressCuttings");
var eSelectedPressCuttingsArray = [];
var iIndexArray = [];
for (var i = 0; i < 7; i++) {
var iIndexArrayValue;
// two conditions being checked in while loop, if random no. is not in global array (iIndexArray) & i var is equal to eSelectedPress... array
while (jQuery.inArray(((iIndexArrayValue = Math.floor(Math.random() * 14) + 1), iIndexArray) === -1)
&& (i === eSelectedPressCuttingsArray.length))
{
// to push a value at a position from array ePressCut... into eSelectedPress... array
eSelectedPressCuttingsArray.push(ePressCuttingsArray[iIndexArrayValue]);
// run a function to addClass to the recently pushed value in eSelectedPress... array
(function (i) {
$(eSelectedPressCuttingsArray[i]).addClass("cPressCuttingsDisplay0" + i)
} (i) );
iIndexArray.push(iIndexArrayValue);
}
}
Could someone explain why the closure func. is not performing correctly, i.e. it always successfully add the className "cPressCuttingsDisplay00", but doesnt follow that up with a className of "cPressCuttingsDisplay01" for the next loop iteration.
You should be able to accomplish your goal by using a for loop:
var ePressCuttingsArray = $(".cPressCuttings").makeArray();
var eSelectedPressCuttingsArray = [];
for (var i = 0; i < 7; i++) {
var idx = Math.floor(Math.random() * ePressCuttingsArray.length);
var selectedItem = ePressCuttingsArray[idx];
selectedItem.addClass('cPressCuttingsDisplay0' + i);
eSelectedPressCuttingsArray.push(selectedItem);
ePressCuttingsArray.splice(idx, 1);
}

Javascript Random problem?

var swf=["1.swf","2.swf","3.swf"];
var i = Math.floor(Math.random()*swf.length);
alert(swf[i]); // swf[1] >> 2.swf
This case ,Random output One number.
How to Random output two different numbers ?
var swf = ['1.swf', '2.swf', '3.swf'],
// shuffle
swf = swf.sort(function () { return Math.floor(Math.random() * 3) - 1; });
// use swf[0]
// use swf[1]
Even though the above should work fine, for academical correctness and highest performance and compatibility, you may want to shuffle like this instead:
var n = swf.length;
for(var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = swf[i];
swf[i] = swf[j];
swf[j] = tmp;
}
Credits to tvanfosson and Fisher/Yates. :)
You can use splice to remove the chosen element, then simply select another randomly. The following leaves the original array intact, but if that's not necessary you can use the original and omit the copy. Shown using a loop to demonstrate how to select an arbitrary number of times upto the size of the original array.
var swf=["1.swf","2.swf","3.swf"];
var elementsToChoose = 2;
var copy = swf.slice(0);
var chosen = [];
for (var j = 0; j < elementsToChoose && copy.length; ++j) {
var i = Math.floor(Math.random()*copy.length);
chosen.push( copy.splice(i,1) );
}
for (var j = 0, len = chosen.length; j < len; ++j) {
alert(chosen[j]);
}
I would prefer this way as the bounds are known (you are not getting a random number and comparing it what you already have. It could loop 1 or 1000 times).
var swf = ['1.swf', '2.swf', '3.swf'],
length = swf.length,
i = Math.floor(Math.random() * length);
firstRandom = swf[i];
// I originally used `delete` operator here. It doesn't remove the member, just
// set its value to `undefined`. Using `splice` is the correct way to do it.
swf.splice(i, 1);
length--;
var j = Math.floor(Math.random() * length),
secondRandom = swf[j];
alert(firstRandom + ' - ' + secondRandom);
Patrick DW informed me of delete operator just leaving the value as undefined. I did some Googling and came up with this alternate solution.
Be sure to check Tvanfosson's answer or Deceze's answer for cleaner/alternate solutions.
This is what I would do to require two numbers to be different (could be better answer out there)
var swf=["1.swf","2.swf","3.swf"];
var i = Math.floor(Math.random()*swf.length);
var j;
do {
j = Math.floor(Math.random()*swf.length);
} while (j === i);
alert(swf[i]);
alert(swf[j]);
Edit: should be j===i

Categories

Resources