Deleting rows with 0 from a multidimentional array javascript - javascript

i have two sets of arrays that is joined to make it a two dimentional..what i like to do is to delete any rows that have 0 values in price after its joined then sort desending and display in an html table
var desc = new Array();
var desc = ["Water","Heating","Electric","Gas"];
var price = new Array();
var price=["824","325","0","245"];
var sortdesc;
var sortdesc = new Array (2);
for (i = 0; i < desc . length; ++ i)
{
for (var i=0; i < price.length; i++)
{
sortdesc[i] = Array(desc[i], price[i]);
if (price[i] == 0 )
{
sortdesc.splice(i,1);
}
}
}
sortdesc.sort(function(a,b){ return b[1] - a[1]; });
function sortedtable (array)
{
document . write("<table border>");
var row;
for (row = 0; row < array . length; ++ row)
{
document . write(" <tr>");
var col;
for (col = 0; col < array [row].length; ++ col)
document . write(" <td>" + array [row] [col] + "</td>");
document . write(" </tr>");
}
document.write("</table>");
}
sortedtable(sortdesc);
the questions are i thought the .splice() will restructure the array what did i do wrong?
and is there a better way to do this
i saw other question but they all said use .splice() instead of delete.array[element] but the .splice is not working for me
please pardon my code beginner here.

splice successfully removes the item from the array, yet in the next loop turn you are assigning to sortdesc[i] again so the the removed index will stay undefined - you created a sparse array.
Apart from that, you have a big problem with your nested loops which use the same count variable. It does not end up in an infinite loop at least since price.length >= desc.length, but the construct is highly questionable.
To solve you problems, just add new elements (and only if you really want to add them) to the end of the sortdesc array by using push():
// no need to double initialize
var desc = ["Water","Heating","Electric","Gas"];
var price = ["824","325","0","245"];
// the Array constructor does not take dimensions. You just want an empty array here
var sortdesc = [];
// … to fill it with other arrays:
for (var i = 0, l = Math.min(desc.length, price.length); i < l; i++) {
if (price[i] != 0) {
sortdesc.push( [desc[i], price[i]] );
}
}

Short answer - you shouldn't need to splice/delete from the array, just do a check beforehand and do not add items if the price is zero.
Another quick comment - from your data, it looks like both the desc and price arrays will have the same length. In this case, you can just use one for loop, no need to iterate through both.
Working code:
var desc = ["Water","Heating","Electric","Gas"];
var price = ["824","325","0","245"];
var sortdesc = [];
// Presuming both arrays with be the same length
for (i = 0; i < desc.length; ++ i) {
if(price[i] !== "0") {
sortdesc.push([desc[i], price[i]]);
}
}
sortdesc.sort(function(a,b){ return b[1] - a[1]; });
function sortedtable (array)
{
document . write("<table border>");
for (row = 0; row < array.length; ++ row)
{
document . write(" <tr>");
for (col = 0; col < array[row].length; ++ col)
document . write(" <td>" + array [row] [col] + "</td>");
document . write(" </tr>");
}
document.write("</table>");
}
sortedtable(sortdesc);

Related

Log repeated values in 2d array

I'm trying to optimise a whole school timetable. I have the timetable currently organised in a sheet. Teacher initials are the headings for each column and each row corresponds to a single teaching period in a 30 lesson week. Each cell contains the name of a class.
Currently I am looking for classes that are split between 2 teachers.
I am trying to make an appscript that will log the classname if it appears anywhere outside the current column (i.e. the same class is being taught by 2 or more different teachers at different times)
I'm aware that nesting loops is not the least efficient way of doing this but I just wanted to hack something together quickly to get the job done. Unfortunately this code is taking longer than the maximum permitted time. The array is only 30 rows by about 56 columns so I dont see why it's taking such a long time. (Cant see anything that's obviously infinite about my loops either)
Can anyone help? :)
function splitClassLocator()
{
//copy the sheet to a 2d array.
//(1)descend through each column from vertical idx 3 to period6 idx36
//(2)start at horiz idx 1, descend through each item vertically.
//if item from loop 1 matches item from loop 2 and loop 1 vertical index != loop 2 vertical index
//log the item (split class)
//GET THE DATA
var sh0 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var range = sh0.getDataRange();
var data = range.getValues();
//COMPARE
//main row mr, main col mc V compare row cr, compare col cc
Logger.log("Rows = " + data.length + "cols = " + data[0].length);
for (var mr = 2; mr < data.length; mr ++)
{
for (var mc = 1; mc < data[0].length; mc ++)
{
for (var cr = 2; cr < data.length; cr ++)
{
for (var cc = 1; cc, data[0].length; cc ++)
{
if (mc != cc) // if it's not comparing classes belonging to the same teacher
{
if ((data[mr][mc] != undefined) || (data[mr][mc] != null) || (data[mr][mc] != ""))
{
if (data[mr][mc] == data[cr][cc])
{
Logger.log(data[mr][mc]);
}
}
}
}
}
}
}
}
Rather than discard the information after your comparison, store it and use it! Welcome to the world of not-Arrays!
Your stated goal is to find classes that have more than two teachers. Thus, the minimum you need to do is tally this information on a single trip through the array.
function countTeachersPerClass() {
const schedule = SpreadsheetApp.getActive().getSheetByName("somename").getDataRange().getValues();
const headers = schedule.shift();
const numTeachers = headers.length;
// Store classes as properties in an Object.
const classes = {};
// Read the schedule array.
// Assumption: time is in index 0, classes in all other.
schedule.forEach(function (period) {
var name = period[0];
// Store each class in this period.
for (var teacherIndex = 1; teacherIndex < numTeachers; ++teacherIndex) {
// Have we seen this class? If no, initialize it.
var classID = period[teacherIndex];
if (!classes[classID])
classes[classID] = {teachers: {} };
// Add this teacher to its list of teachers.
var tID = headers[teacherIndex];
if (!classes[classID].teachers[tID])
classes[classID].teachers[tID] = {periods: []};
// Add this period for this teacher.
classes[classID].teachers[tID].periods.push(name);
} //End for loop over columns in a row.
}); // End forEach over schedule's rows.
// Log the object (in Stackdriver, for easy review and interactivity).
console.log({message: "Built classes object", input: schedule, result: classes, teachers: headers});
// Make a report from it.
const report = [["Class ID", /* other headers */]];
for (var cID in classes) {
// This particular report is for class with more than two teachers.
if (Object.keys(classes[cID].teachers).length > 2) {
var row = [cID];
/** Push info to the row, perhaps with just the names
of the teachers, or also including number of
periods per each teacher, etc. */;
// Add the completed report row to the report.
report.push(row);
}
}
// Log the report.
console.info({message: "Report", report: report});
}
You could certainly get fancier by adding more properties to each classes object than just teachers, such as tracking the average consecutive taught time (i.e. which classes alternate teachers often), but I leave that as an exercise to the reader :)
This works a bit better, flattening to make 2 single dimensional arrays, one is the teacher to check and the other array is all the other timetables combined.
Still takes ages though:
function splitClasses()
{
var sh0 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
var range = sh0.getDataRange();
var data = range.getValues();
var rows = data.length;
var cols = data[0].length;
for (var col = 1; col < cols; col ++)
{
var teacherTT = [];
var othersTT = [];
for (var row = 2; row < rows; row ++)
{
teacherTT.push(data[row][col]);
}
for (var oCol = 1; oCol < cols; oCol ++)
{
for (var oRow = 2; oRow < rows; oRow ++)
{
if (col !=oCol)//dont add current teacher TT to others TT
{
othersTT.push(data[oRow][oCol]);
}
}
}
//Logger.log(othersTT);
var tLength = teacherTT.length;
var oLength = othersTT.length;
Logger.log("tL" + tLength);
Logger.log("oL" + oLength);
for (var t = 0; t < tLength; t ++)
{
//Logger.log("t "+t);
for (var o = 0; o < oLength; o ++)
{
if (teacherTT[t] != undefined ||teacherTT[t] != null || teacherTT[t] != "" || teacherTT[t] != " ")
{
if (teacherTT[t])
{
if (teacherTT[t] == othersTT[o])
{
//Logger.log("o "+o);
Logger.log(teacherTT[t]);
}
}
}
}
}
}
}

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];
}
}

Put a line break in a for loop that is generating html content

I have a for loop that is generating some HTML content:
var boxes = "";
for (i = 0; i < 11; i ++) {
boxes += "<div class=\"box\"><img src=\"unlkd.png\"/></div>";
}
document.getElementById("id").innerHTML = boxes;
I want to display 3 boxes in one row, then below them 2 boxes in one row, then 1, then 3 again, 2, and 1.
First i thought of using the if statement to check whether i > 2 to add a line break, but it will also add a line break after every box past the third one. Nothing comes to mind, and my basic knowledge of javascript tells me I'll have to make a loop for each row I want to make. Any advice?
I would use a different approch :
Use a array to store the number of item per row :
var array = [3, 2, 1, 3, 2];
Then, using two loops to iterate this
for(var i = 0; i < array.length; i++){
//Start the row
for(var j = 0; j < array[i]; ++j){
//create the item inline
}
//End the row
}
And you have a pretty system that will be dynamic if you load/update the array.
PS : not write javascript in a while, might be some syntax error
Edit :
To generate an id, this would be simple.
create a variable that will be used as a counter.
var counter = 0;
On each creating of an item, set the id like
var id = 'boxes_inline_' + counter++;
And add this value to the item you are generating.
Note : This is a small part of the algorithm I used to build a form generator. Of course the array contained much more values (properties). But this gave a really nice solution to build form depending on JSON
You can try something like this:
Idea
Keep an array of batch size
Loop over array and check if iterator is at par with position
If yes, update position and index to fetch next position
var boxes = "";
var intervals = [3, 2, 1];
var position = intervals[0];
var index = 0;
for (i = 0; i < 11; i++) {
boxes += "<div class=\"box\"><img src=\"unlkd.png\"/></div>";
if ((position-1) === i) {
boxes += "<br/>";
index = (index + 1) % intervals.length;
position += intervals[index]
}
}
document.getElementById("content").innerHTML = boxes;
.box{
display: inline-block;
}
<div id="content"></div>
var boxes = "",
boxesInRow = 3,
count = 0;
for (i = 0; i < 11; i ++) {
boxes += "<div class=\"box\"><img src=\"unlkd.png\"/></div>";
count++;
if(count === boxesInRow) {
boxes += "<br/>";
boxesInRow -= 1;
count = 0;
if (boxesInRow === 0) {
boxesInRow = 3;
}
}
}
document.getElementById("id").innerHTML = boxes;
var i;
var boxes = "";
for (i = 0; i < boxes.length; i++) {
boxes += "<div class=""><img src=""/></div>";
function displayboxes() {
"use strict";
for (i = 0; i < boxes.length; i++) {
out.appendChild(document.createTextNode(boxes[i] + "<br>"));
}
}
displayboxes(boxes);

Hightlight differences between items in table row

I'm trying to add to a HTML table a feature that highlights all those values that, compared to others, are different. Comparison is made row by row.
With great effort I managed to achieve the following JQuery/Javascrit code. I'm pretty sure this is not an efficient/elegant/fast way to do it but it's the only way I work it out.
The HTML table is quite big and complex so it's hard to publish it here.
The issue I'm encountering is that the script works fine out of a loop, but it hangs if I put it inside a FOR - LOOP and I don't understand why.
var numRows = $('.ctable tbody tr').length, numCols = $('.ctable tbody tr:first th').length, v, undefined;
var values = new Array(numRows);
var noDuplicates = new Array(numCols);
var result = new Array(numCols);
for (i = 1; i = numRows; i++) {
// Get a row and copy into an array the values of each VISIBLE cell
$(".ctable tbody tr:eq(" + i + ") td.values:visible").each(function(){
v = $(this).text();
values.push(v.trim());
});
// Remove from the array the 'undefined' values
values = values.filter(function(item){
return item !== undefined;
});
// Push into new array duplicate values
noDuplicates = return_duplicates(values);
// Compare the two arrays and get the differences (uses underscore.js)
result = _.difference(values, noDuplicates);
// This is a 'highlight' plugin and you may pass to it an array
$(".ctable tbody tr:eq(" + i + ") td.values:visible").highlight(values);
}
function return_duplicates(arr) {
var len=arr.length, out=[], counts={};
for (var i=0;i<len;i++) {
var item = arr[i];
counts[item] = counts[item] >= 1 ? counts[item] + 1 : 1;
}
for (var item in counts) {
if(counts[item] > 1)
out.push(item);
}
return out;
}
Try
for (i = 1; i < numRows; i++) {
instead of
for (i = 1; i = numRows; i++) {

Why does the sort() function changes values of numbers in this Array?

var _txtString = ":un:-:un:-:deux:-:deux:-:deux:-:trois:-:trois:" ;
var _array = ["un", "deux", "trois"] ;
var _items = new Array();
for (var t =0; t < _array.length; t++) {
found = _txtString.match(new RegExp(':' + _array[t]+ ':', 'g'));
_items[t] = parseInt(found.length);
//_items.sort();
document.write("<br />" + _items[t] + " " + _array[t]);
}
Hi,
when I run this code, results displayed are properly counted:
2 un
3 deux
2 trois
But when I uncomment the sort() line, count is wrong:
2 un
3 deux
3 trois <=
What I wanted is to sort the result returned by numeric value. What is beyound my understanding is that the sort() function changes the actual value ?! Any clue why ?
Thanks
Because you are sorting, you are changing the order of the array. So when you sort the "3" becomes the last index and it writes that out.
_items[t] = parseInt(found.length); //[2,3,2]
_items.sort(); //[2,2,3]
document.write("<br />" + _items[t] + " " + _array[t]); //here you are reading the last index which is 3
If you want to sort by the count, you need to do it after you calculate everything.
Basic idea:
var _txtString = ":un:-:un:-:deux:-:deux:-:deux:-:trois:-:trois:";
var _array = ["un", "deux", "trois"];
var _items = new Array();
for (var t = 0; t < _array.length; t++) {
found = _txtString.match(new RegExp(':' + _array[t] + ':', 'g'));
_items.push({
count: found.length,
text: _array[t]
});
}
_items.sort(function (a, b) {
return a.count - b.count;
});
for (var i = 0; i < _items.length; i++) {
console.log(_items[i].count, _items[i].text);
}
The sort command in javascript does an in-place sort, meaning it will mutate your array order. When this occurs it simply looks to me like your code is just out of sync to what you are expecting.
There is no way to avoid this unless you make a copy of the array and do a sort on the copy therefore leaving the original array as-is.

Categories

Resources