Trying to read this Javascript loop - javascript

I'm trying to read this code. As far as I can tell, it's checking if the child element is "at position" (but it's equal to a string? That makes no sense to me). If it isn't, then it adds 2 to the offset, if it is, it does nothing.
var p = document.getElementById(parent);
var c = document.getElementById(child );
var top = (c["at_position"] == "y") ? p.offsetHeight+2 : 0;
var left = (c["at_position"] == "x") ? p.offsetWidth +2 : 0;
And then here, this for loop does not make sense to me - why does it start with a semicolon?
for (; p; p = p.offsetParent)
{
top += p.offsetTop;
left += p.offsetLeft;
}
Can anyone clarify how to read this a little bit better? I'm trying to reduce the distance between the parent and the child to a limited extent (Only for "left")

It loops while p is truethy that is, p is not null because offsetParent will likely be null once your reach the top.
It's equivalent to:
var p = document.getElementById(parent);
while (p !== null) {
top += p.offsetTop;
left += p.offsetLeft;
p = p.offsetParent;
}
Normally a for loop looks like this:
for (var i = 0; i < array.length; i += 1) {
the first bit var i = 0 is the initialization part. You can leave it empty like:
var i = 0;
for (; i < array.length; i += 1) {
You can also do this:
for (var i = 0, j = 10 - i; i < array.length; i += 1, j -= 1) {
Although there are perhaps better ways to do it. Just showing you the syntax :)
The second part is the part that must be truethy, if it's false the for loop will stop. This can't be empty of course.
The last part is the incrementer part, which can also be empty. So you could have:
var p = document.getElementById(parent);
for (; p ;) {
p = p.offsetParent;
}
It looks weird, but works. I wouldn't recommend this though because it looks weird, and by that I mean that it looks like a bug.

Related

Remove duplicates from an array in Javascript (without modify the array) - Understand the logic behind

I started using a website called leetcode and one question is to remove all the duplicates in an array without create a new one. Here is the question https://leetcode.com/problems/remove-duplicates-from-sorted-array/description/
My solution was to loop and the check each element against the next one, then if match use splice to remove the duplicated one. it works but not when you have something like [1,1,1,1,1] or [1,1,2,2,2,2,3,3] so I found on github a working code:
var removeDuplicates = function(nums) {
var i = 0;
for (var n in nums)
if (i === 0 || nums[n] > nums[i-1])
nums[i++] = nums[n];
return i;
};
This code works and passes all the 160 tests, but I don't understand clearly what is doing, especially the part in nums[i++] = nums[n]; can someone be so kind to help me to understand what this, simple, code is doing? thanks
Consider this code that creates a new array:
function removeDuplicates(nums) {
var res = [];
var i = 0;
for (var j=0; j<nums.length; j++)
if (i === 0 || nums[j] !== res[i-1])
res[i++] = nums[j];
return res;
}
The line you're asking about assigns nums[j] as new element at res[i] when the value is not the same as the previous one (res[i-1]), then increments i to put the next non-duplicate value in the next position.
Now we use the same algorithm but instead of assigning to a new res array, we modify the original nums array:
function removeDuplicates(nums) {
var i = 0;
for (var j=0; j<nums.length; j++)
if (i === 0 || nums[j] !== nums[i-1])
nums[i++] = nums[j];
nums.length = i; // drop the rest
}
Given that j >= i is guaranteed, we only modify array elements that we've always visited, so there's no harm in writing on the same array that we are reading from.

Javascript print square using for loop and conditional statement only

Just started my uni course, struggling a little with javascript. I have been asked to display a square using any character, however, the solution must combine for loops and if statements.
This is what I have so far and I feel pretty close but I just can't get the second line to display. I know this can be done via two for loops, (one for iteration of the variable and another for spaces). But this is not how I have been asked to solve this problem.
Here is my code:
var size = 3;
let i;
for(i = 0; i < size; i++) {
print ("*");
if (size === i){
println ("");
}
}
For context, this is all taking place int he professors homemade learning environment.
You could use nested for loops and take a line break after each filled line.
function print(s) { document.getElementById('out').innerHTML += s; }
function println(s) { document.getElementById('out').innerHTML += s + '\n'; }
var size = 5,
i, j;
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
print("*");
}
println("");
}
<pre id="out"></pre>
Single loop with a check if i is unequal to zero and if the remainder is zero, then add a line break.
Using:
=== identity/strict equality operator checks the type and the value, for example if both are numbers and if the value is the same,
!== non-identity/strict inequality operator it is like above, but it checks the oposite of it,
% remainder operator, which returns a rest of a number which division returns an integer number.
&& logical AND operator, which check both sides and returns the last value if both a truthy (like any array, object, number not zero, a not empty string, true), or the first, if it is falsy (like undefined, null, 0, '' (empty string), false, the oposite of truthy).
function print(s) { document.getElementById('out').innerHTML += s; }
function println(s) { document.getElementById('out').innerHTML += s + '\n'; }
var size = 5,
i;
for (i = 0; i < size * size; i++) {
if (i !== 0 && i % size === 0) {
println("");
}
print("*");
}
<pre id="out"></pre>
Well the for loop is only iterating 3 times, printing the first line. If you want a square you'll have to print 9 stars total, right? So i'm assuming, is this is the approach you'd go for, you would need to iterate not until size, but until size * size.
I'm using console.log to 'print' the square:
var dimension = 10;
var edge = '*';
var inside = ' ';
var printLine;
for (var i = 1; i <= dimension; i++) {
if (i === 1 || i === dimension) {
printline = Array(dimension + 1).join(edge);
} else {
printline = edge + Array(dimension - 1).join(inside) + edge;
}
console.log(printline);
}
Note that in the following example, an array of length 11 gets you only 10 "a"s, since Array.join puts the argument between the array elements:
Array(11).join('a'); // create string with 10 as "aaaaaaaaaa"
You wanna make a square of * where the size is the number of * on its sides?
Let's split a task into 3 parts:
where you print top side like *****
where you print middle (left and right sides) like * *
where you print bottom (same as top)
Now let's code that, I kept the code as simple as possible, this can be done in fewer lines but I think this will be easier to understand for beginners:
var size = 5;
var i = 0;
// top
for (i = 0; i < size; i++)
console.log("*");
//middle
for (var j = 0; j < size - 2; j++){
console.log("\n"); // go to next row
// middle (2 on sides with size-2 in between)
console.log("*");
for (i = 0; i < size-2; i++)
console.log(" ");
console.log("*\n"); // goes to new row as well
}
// same as top
for (i = 0; i < size; i++)
console.log("*");
Full square is even simpler:
var size = 5;
var i = 0;
for (var i = 0; i < size; i++){ // iterates rows
for (var j = 0; j < size; j++) // iterates * in row
console.log("*");
console.log("\n") // moves to new row
}
In order to print a row, you print same sign X times. Well, to print X rows we can use just that 1 more time (only this time we are iterating over a different variable (j is * in a row, i is a number of rows).
After a row is made we go to go to next row with \n.
As for
it must contain if statement
Put this at the end:
if (youCanHandleTheTruth) console.log("It's a terrible practice to tell students their solution MUST CONTAIN CODEWORDS. If you need them to showcase something, write appropriate task that will require them to do so.");

How to shorten these duplicate JavaScript code into a loop?

I had ten rows which each rows contain 4 column, now I want to get the value which I had import using localStorage. I find a way to put all these value independently but the code is all the repeat one. These will cause to redundancy of code. I wonder if there are a way to shorten the code using loop?
Here is my code
var res = {};
$(function(){
$('#subbtn').click(function() {
console.log($('#tab').find('tr'))
$('tr').each(function(){
var tmp = [];
var cl ;
$(this).find('select').each(function(){
cl = $(this).attr('class');
//console.log(cl);
tmp.push($(this).val());
})
res[cl] = tmp
})
console.log(res);
localStorage.setItem("testingvalue",JSON.stringify(res));
document.getElementById("results__display").innerHTML = (localStorage.getItem("testingvalue"));
})
})
$( document ).ready(function(){
var res = {};
try {
console.log('existed');
res = JSON.parse(localStorage.getItem("testingvalue"));
//alert(res.r1[2]);
document.getElementsByClassName("r1")[0].selectedIndex=res.r1[0];
document.getElementsByClassName("r1")[1].selectedIndex=res.r1[1];
document.getElementsByClassName("r1")[2].selectedIndex=res.r1[2];
document.getElementsByClassName("r1")[3].selectedIndex=res.r1[3];
document.getElementsByClassName("r2")[0].selectedIndex=res.r2[0];
document.getElementsByClassName("r2")[1].selectedIndex=res.r2[1];
document.getElementsByClassName("r2")[2].selectedIndex=res.r2[2];
document.getElementsByClassName("r2")[3].selectedIndex=res.r2[3];
document.getElementsByClassName("r3")[0].selectedIndex=res.r3[0];
document.getElementsByClassName("r3")[1].selectedIndex=res.r3[1];
document.getElementsByClassName("r3")[2].selectedIndex=res.r3[2];
document.getElementsByClassName("r3")[3].selectedIndex=res.r3[3];
document.getElementsByClassName("r4")[0].selectedIndex=res.r4[0];
document.getElementsByClassName("r4")[1].selectedIndex=res.r4[1];
document.getElementsByClassName("r4")[2].selectedIndex=res.r4[2];
document.getElementsByClassName("r4")[3].selectedIndex=res.r4[3];
document.getElementsByClassName("r5")[0].selectedIndex=res.r5[0];
document.getElementsByClassName("r5")[1].selectedIndex=res.r5[1];
document.getElementsByClassName("r5")[2].selectedIndex=res.r5[2];
document.getElementsByClassName("r5")[3].selectedIndex=res.r5[3];
document.getElementsByClassName("r6")[0].selectedIndex=res.r6[0];
document.getElementsByClassName("r6")[1].selectedIndex=res.r6[1];
document.getElementsByClassName("r6")[2].selectedIndex=res.r6[2];
document.getElementsByClassName("r6")[3].selectedIndex=res.r6[3];
document.getElementsByClassName("r7")[0].selectedIndex=res.r7[0];
document.getElementsByClassName("r7")[1].selectedIndex=res.r7[1];
document.getElementsByClassName("r7")[2].selectedIndex=res.r7[2];
document.getElementsByClassName("r7")[3].selectedIndex=res.r7[3];
document.getElementsByClassName("r8")[0].selectedIndex=res.r8[0];
document.getElementsByClassName("r8")[1].selectedIndex=res.r8[1];
document.getElementsByClassName("r8")[2].selectedIndex=res.r8[2];
document.getElementsByClassName("r8")[3].selectedIndex=res.r8[3];
document.getElementsByClassName("r9")[0].selectedIndex=res.r9[0];
document.getElementsByClassName("r9")[1].selectedIndex=res.r9[1];
document.getElementsByClassName("r9")[2].selectedIndex=res.r9[2];
document.getElementsByClassName("r9")[3].selectedIndex=res.r9[3];
document.getElementsByClassName("r10")[0].selectedIndex=res.r10[0];
document.getElementsByClassName("r10")[1].selectedIndex=res.r10[1];
document.getElementsByClassName("r10")[2].selectedIndex=res.r10[2];
document.getElementsByClassName("r10")[3].selectedIndex=res.r10[3];
}
catch (error){
console.log(error.message);
}
});
Looking at this repeated line:
document.getElementsByClassName("r1")[0].selectedIndex=res.r1[0];
...a simple first pass improvement would be to just use a nested for loop with variables instead of "r1" and 0:
for (var r = 1; r <= 10; r++) {
for (var i = 0; i < 4; i++) {
document.getElementsByClassName("r" + r)[i].selectedIndex = res["r" + r][i];
}
}
Notice, though, that this means the .getElementsByClassName("r" + r) call happens four time for each value of r, which is not very efficient - it would be better to move that into the outer loop:
var els;
for (var r = 1; r <= 10; r++) {
els = document.getElementsByClassName("r" + r);
for (var i = 0; i < 4; i++) {
els[i].selectedIndex = res["r" + r][i];
}
}
In the second version the inner loop could say i < els.length rather than i < 4, although note that either way you need to be sure you match the number of HTML elements to the number of items in your res object.
You've seem to have the jQuery library loaded. Using jQuery makes this much easier.
Here is an example:
var res = JSON.parse(localStorage.getItem("testingvalue"));
$("tr select").each(function(){
$(this).val(res[$(this).attr("class")][$(this).index()]);
});
Of course, this will only work if the select elements have only one class name and the res object contains values for all the select elements that are inside tr elements. Based on the jQuery code in your question that seems to be the case.
And this is a safer approach
Object.keys(res).forEach(function(key){
res[key].forEach(function(val, index){
$("tr select." + key).eq(index).val(val);
});
});
Code below will work regardless the size of your data in storage:
res = JSON.parse(localStorage.getItem("testingvalue"));
// Let's start with checking 'res' type.
// - if it's an Array, get the the length from .length
// - if it's Object, get the the length from Object.keys().length
var resLength = Array.isArray(res) ? res.length : typeof res === 'object' ? Object.keys(res).length : 0;
// loop throw the rows.
for (var i = 0; i < resLength; i++) {
// Do the same as above: get type of the row and calculate it length for the loop.
var rowLength = Array.isArray(res[i]) ? res.length : typeof res[i] === 'object' ? Object.keys(res[i]).length : 0;
// loop throw the columns on the row.
for (var j = 0; j < rowLength; j++) {
document.getElementsByClassName('r'+i)[j].selectedIndex=res['r'+i][j];
}
}

how to check which elements of an array match relative to position

Trying to create a function or two that will be able to check the elements of an array and output wheater the elements of the two arrays are identical (ie same number and identical position is present), or the number is present but does not match the same position as the other array. Basically, I'm attempting to recreate a simple game called mastermind. The main problem im having is a case senarior when say the right answer is [1,2,3,4] and the user will guess [0,1,1,1], my function will out put that the number 1 is present 3 times, and I need to figure out how to just have it say the number 1 is present 1 time. Here is the function that checks the arrays:
function make_move(guess, answ){
var myguess = document.getElementById("mymoves");
var correct_number_correct_spot= 0;
var correct_number_wrong_spot= 0;
for(var i = 0; i < 4; ++i)
{
if(answ[i] == guess[i]){
++correct_number_correct_spot;
}
else if(answ[i] !== guess[i] && $.inArray(guess[i], answ) !== -1){
++correct_number_wrong_spot;
}
}
console.log(answ);
console.log(guess);
myguess.innerHTML += correct_number_correct_spot + " were right!" +correct_number_wrong_spot+ "there but not in the right order";
}
You can keep the count of missed numbers in an object, and subtract the guessed ones that appear in the answer. Then you can calculate the correct_number_wrong_spot subtracting the number of correct_number_correct_spot and the missed ones.
function make_move(guess, answ){
var myguess = document.getElementById("mymoves");
var correct_number_correct_spot = 0;
// Initialize missed counts to the numbers in the answer.
var correct_number_wrong_spot = answ.length;
var missed = {};
for (var j = 0; j < answ.length; j++) {
missed[answ[j]] = (missed[answ[j]] || 0) + 1;
}
for(var i = 0; i < answ.length; ++i)
{
if(answ[i] == guess[i]){
++correct_number_correct_spot;
}
// Subtract the guessed numbers from the missed counts.
if (guess[i] in missed) {
missed[guess[i]] = Math.max(0, missed[guess[i]] - 1);
}
}
// Subtract the correctly spotted numbers.
correct_number_wrong_spot -= correct_number_correct_spot;
// Subtract the remaining missed numbers.
for (var number in missed) {
correct_number_wrong_spot -= missed[number];
}
console.log(answ);
console.log(guess);
myguess.innerHTML += correct_number_correct_spot + " were right!" +correct_number_wrong_spot+ "there but not in the right order";
}
Check demo
EDIT: My try to explain doubts exposed in the comments:
would you mind explining how this code works: for (var j = 0; j < answ.length; j++) { missed[answ[j]] = (missed[answ[j]] || 0) + 1; }
missed[answ[j]] = (missed[answ[j]] || 0) + 1;
This is a quick way to increment the count for a number or initialize it to 0 if it doesn't exists yet. More or less the statement works like this:
If missed[answ[j]] is undefined then it is falsy and hence the || (or operator) evaluates to the 0. Otherwise, if we already have a value greater than 0, then it is truthy and the || evaluates to the contained number.
If it looks weird, you can replace this line with:
if (!(answ[j] in missed)) {
missed[answ[j]] = 0;
}
missed[answ[j]] += 1;
also if (guess[i] in missed) { missed[guess[i]] = Math.max(0, missed[guess[i]] - 1);
missed[guess[i]] = Math.max(0, missed[guess[i]] - 1);
In this case I use Math.max to make sure we don't subtract below 0. We don't want repeated numbers in the guess that exceeds the number of those present in the answer count. I mean, we subtract at most until the number of repeated numbers in the answer.
if (missed[guess[i]] > 0) {
missed[guess[i]] -= 1;
}
Try this fiddle!
Without changing your original function too much, you can use an object as a map to keep track of which numbers you have already matched.
var number_matched = {};
// ...
if(!number_matched[guess[i]]) {
number_matched[guess[i]] = true;
}

More Efficient Way to Accomplish This?

I need to know the level of a player using the amount of exp he has and the exp chart. I want to do it the most efficient way possible. This is what I got. Note: The real expChart has thousands of levels/index. All the values are in increasing order.
var expChart = [1,10,23,54,65,78,233,544,7666,22224,64654,456456,1123442];
/*
lvl 0: //0-1[ exp
lvl 1: //[1-10[ exp
lvl 2: //[10-23[ exp
*/
getLvlViaExp = function(exp){
for(var i = 0 ; i < expChart.length ; i++){
if(exp < expChart[i]) break;
}
return i;
}
This is a more efficient way to do it. Every x steps, (6 i the example, probably every hundreds with real chart), I do a quick comparation and jump to approximative index, skipping many indexes.
getLvlViaExp = function(exp){
var start = 0;
if(exp > 233) start = 6;
if(exp > 1123442) start = 12;
for(var i = start ; i < expChart.length ; i++){
if(exp < expChart[i]) break;
}
return i;
}
Is there an even better way to do this?
SOLUTION:
Array.prototype.binarySearch = function(value){
var startIndex = 0,
stopIndex = this.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
if(value < this[0]) return 0;
while(!(value >= this[middle] && value < this[middle+1]) && startIndex < stopIndex){
if (value < this[middle]){
stopIndex = middle - 1;
} else if (value > this[middle]){
startIndex = middle + 1;
}
middle = Math.floor((stopIndex + startIndex)/2);
}
return middle+1;
}
The best algorithm for searching is binary search which is O(lg n) (unless you can do it with a hashing search which is O(c).
http://www.nczonline.net/blog/2009/09/01/computer-science-in-javascript-binary-search/
Basically jump to the middle of your chart ( n / 2). Is you experience higher or lower from that number. If higher jump to the middle higher half. If lower jump to the middle of the lower half: Compare and repeat until you find what you're looking for.
var expChart = [1,10,23,54,65,78,233,544,7666,22224,64654,456456,1123442];
getLvlViaExp = function(exp){
var min=0;
var max=expChart.length-1;
var i;
while(min <=max){
i=Math.round((min+max)/2);
//document.write("<br />"+i+":"+min+":"+max+"<br />");
if(exp>=expChart[i] && exp <=expChart[i+1]) {
break;
}
if(exp>=expChart[i]){
min=i+1;
}
if(exp<=expChart[i]){
max=i-1;
}
}
return i;
}
document.write(getLvlViaExp("10"));
I have tested it and it seems to work pretty well. If you want to see how many steps it actually goes through to get to the answer, uncomment the document.write in the while loop. It was kind of fascinating watching it.

Categories

Resources