What I am trying to achieve is to allocate a panel to the column with the least amount of panels in it, and if 2 of the columns have an equal amount of panels then I take the first column and insert it there. I have attempted to implement this, which I will show below however I want to first address the problem that I am having. I understand that my code may have many flaws, and so I am open to suggestion on how to make it better. so the error I am getting through firebug is
TypeError: columns[leastItems] is undefined
return columns[leastItems].id;
and here is my javascript code that I have implemented:
function DashboardAllocation() {
var columns = document.querySelectorAll('.col-md-4.column');
var noElemsInCol = new Array(columns.length);
//Get count of children in each column into array
for (var columnNumber = 0; columnNumber < columns.length; columnNumber++) {
noElemsInCol.push(countChildren(columns[columnNumber]));
}
//Compare all values to see if they are all the same or if the first column is 0
if (compareAllValues(noElemsInCol) || countChildren(columns[0] === 0)) {
//if it is then return the first columns id
return columns[0].id;
}
//Reference http://www.programmingsimplified.com/c/source-code/c-program-find-minimum-element-in-array
var leastItems = 1;
var minimum = countChildren(columns[0]);;
for (var i = 1; i < noElemsInCol.length; i++) {
if (noElemsInCol[i] < minimum) {
minimum = noElemsInCol[i];
leastItems = i + 1;
}
}
return columns[leastItems].id;
}
//Compares all the values in the array to check if they are equal
//Reference http://stackoverflow.com/questions/9973323/javascript-compare-3-values
function compareAllValues(a) {
for (var i = 0; i < a.length; i++) {
if (a[i] === null) { return false }
for (var j = 0; j < i; j++) {
if (a[j] !== a[i]) { return false }
}
}
return true;
}
function countChildren(Nodes) {
var childrenCount = 0;
for (var nodeType in Nodes.childNodes) {
//if the nodetype is an element then we will add one
if (nodeType == Node.ELEMENT_NODE) {
childrenCount++;
}
}
return childrenCount;
}
I have referenced the sources where I took code from and hope it helps to see where I am coming from and understand what I am trying to do
You are getting problem as an index leastItems of the array columns is out of range.
Currently You are getting error as in an array of [5] you are trying to fetch the sixth element, using leastItems variable
So either use change
var leastItems = 0;
OR, Use
return columns[leastItems -1 ].id;
Related
I try to code a Connect Four game and define a winner when 4 pieces are aligned horizontally. I haven't moved to the diagonal part yet, want to get this one first.
What I try to achieve - check if the current location (i) and i+6, meaning the next column, same row, have the same player's class.
Problem - "Count" never adds up for horizontal victory.
The problematic part starts from checkForHorizontalVictory.
Pay attention that I switched for loop to go through allSlots.length, not slots.length, as I want all 42 slots. You'll see the rest.
Anyway, how do I write a code to understand where the player's button is, get the index, and check if index+6 (and later -6) has the same class?
var currentPlayer = "player1";
var column = $(".column");
var allSlots = $(column.children());
function switchPlayer() {
if (currentPlayer === "player1") {
currentPlayer = "player2";
} else {
currentPlayer = "player1";
}
}
$(".column").on("click", function (e) {
var col = $(e.currentTarget);
var slots = col.children();
for (var i = slots.length - 1; i >= 0; i--) {
var currentSlot = slots.eq(i);
var hole = currentSlot.children();
if (!hole.hasClass("player1") && !hole.hasClass("player2")) {
hole.addClass(currentPlayer);
var verticalVictory = checkForVerticalVictory(slots);
var horizontalVictory = checkForHorizontalVictory(slots);
if (verticalVictory) {
}
if (horizontalVictory) {
}
switchPlayer();
break;
}
}
});
function checkForVerticalVictory(slots) {
var count = 0;
for (var i = 0; i < slots.length; i++) {
console.log(slots.length, "this is slots length in vertical");
var currentSlot = slots.eq(i);
var hole = currentSlot.children();
if (hole.hasClass(currentPlayer)) {
count++;
} else {
count = 0;
}
console.log(count, "this is count!");
if (count == 4) {
alert("you won! 🎉");
return true;
}
}
console.log("no wins yet ❌");
return false;
}
function checkForHorizontalVictory(slots) {
var count = 0;
for (var i = 0; i < allSlots.length; i++) {
var thisSlot = allSlots.eq(i);
var thisHole = thisSlot.children(i);
if (
thisHole.hasClass(currentPlayer) && allSlots.eq(i + 6).hasClass(currentPlayer)
) {
count++;
console.log("this is count", count);
} else {
count = 0;
}
console.log("this is count", count);
if (count == 4) {
// 3. If the counter reaches 4 - we have a win
alert("you won! 🎉");
return true;
}
}
console.log("no wins yet ❌");
return false;
}
I can't be sure, since I can't reproduce your issue. But here is what I think:
In the for loop, when it reaches the indexes of the last column and checks for i + 6 it makes allSlots.eq(42) to allSlots.eq(47)... And that ends up in a false. So count gets resetted to zero.
So change the loop too:
for (var i = 0; i < allSlots.length - 6; i++) {
I also would move that after the loop. Notice the condition change.
if (count <= 4) {
// 3. If the counter reaches 4 - we have a win
alert("you won! 🎉");
return true;
}
In short, column #7 was already checked while checking column #6... ;)
EDIT
Using the full code from your gitHub, I changed the way you are checking the holes of a particular row.
First, pass the slot index to checkForHorizontalVictory
So in that function, you know exactly which slot is the starting point of your logic.
You can now get all the slots on the same row by filtering allSlots.
I used Arra.from() to be able to use JS .filter with a modulo of the deducted rowNum.
Then, recreate a jQuery object from that filtered element list.
function checkForHorizontalVictory(currentSlotIndex) {
var count = 0;
let rowNum = currentSlotIndex % 6; // rows from 0 to 5, top to bottom
console.log("rowNum", rowNum);
// Get a JS array from the jQuery collection
var thisrow = Array.from(allSlots).filter((s, i) => i % 6 == rowNum);
console.log("thisrow", thisrow);
console.log("thisrow length", thisrow.length);
// Create a jQuery collection from the filtered above
var $thisrow = $(thisrow)
for (var i = 0; i < thisrow.length; i++) {
// var thisrow = allSlots.eq(currentRow);
console.log(thisrow[i]);
// Tests to confirm you are checking the right row
// thisHole.addClass("test");
// thisHole.text(i)
if (
// jQuery object for the specific row
$thisrow
.eq(i + 1)
.children()
.hasClass(currentPlayer)
) {
count++;
}
console.log("this is count", count);
if (count == 4) {
console.log("you won! 🎉");
return true;
}
}
console.log("no wins yet ❌");
return false;
}
CodePen with your modified code
For fun, uncomment the Tests to confirm you are checking the right row.
The while statement keeps throwing an error (TypeError: Cannot read property '8' of undefined) however the console.log still outputs the result that it's meant to.
var hold = [];
for (let i = 0; i < (clothes.length - 1); i++) {
let count = 0;
if (hold.length === 0) {
hold.push(clothes[i]);
} else {
console.log(clothes[i][8], hold[count][8]);
while (clothes[i][8] < hold[count][8]) {
count++;
}
hold.slice(count, 0, clothes[i]);
}
}
This part of the code increments count beyond the length of hold[]
while (clothes[i][8] < hold[count][8]){
count ++;
};
stepping through manually:
clothes[0] gets added as hold[0] by the "if hold is empty clause"
clothes[1] gets compared with hold[0] and is < so count++
clothes[1] gets compared with hold[1], but there's no hold[1], so you get the error
Add a clause in the while
while (count < hold.length && clothes[i][8] < hold[count][8]){
count ++;
};
Note the length check must be first otherwise you still get the same error (there's other ways such as break out of the while). The 2nd part of the && is only valuated if the first part is true.
You have other issues stopping a complete solution:
for (let i = 0; i < (clothes.length - 1); i++){
will loop to the length-1, so if you have 3 elements, you only get two. You need to use either
i<clothes.length
i<=(clothese.length-1)
and
hold.slice(count, 0, clothes[i]);
is not the syntax for .slice and slice returns a new array, does not change the array in place. This should be
hold.splice(count, 0, clothes[i]);
Giving an updated snippet:
var clothes = [[2],[1],[3]];
var hold = []
for (let i = 0; i < clothes.length; i++) {
var count = 0;
if (hold.length === 0) {
hold.push(clothes[i]);
} else {
while (count<hold.length && clothes[i][0] < hold[count][0]) {
count++;
};
if (count == hold.length) {
hold.push(clothes[i])
}
else {
hold.splice(count, 0, clothes[i]);
}
}
}
console.log(hold.join(","));
I have a piece of code that is meant to hide table elements depending on given array. I am running loops to compare innerText of one of the cells and the given array.
However, my if statement is statement is acting weird, once i set the operator to === the operation is successful and table rows that match the array are hidden. But i need my if to hide the elements that are not a part of the array, so naturally I set my operator to !== but once i do it it just executes anyway and of course hides all of the elements in the table.
Any idea why this is happening here is the code:
var td1 = document.querySelectorAll("#course");
var rowss = document.querySelectorAll("#rows");
var courseNames1 = [td1.innerText];
var diff = _.difference(soretedArray, courseNames1)
console.log(diff);
for (var i = 0; i < rowss.length; i++) {
for (var j = 0; j < diff.length; j++) {
if (td1[i].innerText === diff[j]) { // if i set the logic operator to !== it hides all of the elements rather the ones that don't match
console.log(rowss[i]);
rowss[i].style.display = "none";
break;
}
}
}
I added the code as I have understood your request: You want the negation of "contains" to hide the element. This is as complete as possible based on the information you gave.
var soretedArray = [];//initialized elsewhere
var td1 = document.querySelectorAll("#course");
var rowss = document.querySelectorAll("#rows");
function tdContains(td) {
for(var j= 0 ; j< soretedArray.length; j++){
if(td.innerText === soretedArray[j]){
return true;
}
}
return false;
}
for(var i = 0 ; i < rowss.length; i++){
if(!tdContains(td1[i])) {
console.log(rowss[i]);
rowss[i].style.display= "none";
}
}
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];
}
}
I have an array with empty rows,I have the field "codeOperation" to test if a row is empty or not,this is my table:
The client should fill the table whith data and left the rest of rows empty, what I want is when the client click on the "ADD" boutton only the data will be send and the emoty rows will be deleted.
this is my code:
//function to send the Data
$scope.updateGamme= function(gamme) {
gamme.listElementGammeOf = $scope.finalOperationsList;
$scope.DeleteEmptyRows(gamme.listElementGammeOf);
$http
.put(
baseUrl +
"/gamme/update",
gamme)
.success(
function(gammeModifiee) {
//send the Data and update
.....
}); }
//delete the empty rows
$scope.DeleteEmptyRows = function(listelements){
for (var i = 0; i < listelements.length; i++) {
if (listelements[i].operationCode == "")
listelements.splice(i, 1);
}
What I get as a result with this code, is that per example I get 5 items, my code will remove the rows 3 and 4 the row 2 is not deleted
Is there any problem with my code? Please help me find it.
Thanks for help
Looks like
for (var i = 0; i < listelements.length; i++) {
if (listelements[i].operationCode == "")
listelements.splice(i, 1);
}
should be
for (var i = 0; i < listelements.length; i++) {
if (listelements[i].operationCode == "")
listelements.splice(i--, 1);
}
When you iterate and remove items from an array, you should decrement your index not to miss an item after the index shift due to removing.
Try splicing in reverse order. i.e remove rows from the last one.
I haven't tried your code but it must work.
$scope.DeleteEmptyRows = function(listelements){
for (var i = listelements.length-1; i >=0; i--) {
if (listelements[i].operationCode == "") {
listelements.splice(i, 1);
}
}
}
The example I tried is...
var array = ["1","2","","",""];
for(var i=array.length-1;i>=0;i--)
{
if(array[i]=="")
array.splice(i,1);
}