Simple Google Spreadsheet For Each Loop? - javascript

It's been a long time since I've coded. I've never looked into scripts for google spreadsheets before. I just want to make a simple effect to edit the spreadsheet. If I understand correctly, this is doable so long as you run it manually?
The syntax is throwing me off too much. My basic goal is to set each cell in a fixed column range to equal itself plus the value in the adjacent column, and then set that second value to 0.
My instinct would be to do something such as
CellRange[i][j] selected = C9:D13;
for(i=0,i<selectedrange.length,i++){
SpreadsheetApp.getActiveRange().setValue(selected[i][j]+selected[i][j+1];
SpreadsheetApp.getRange(selected[i][j+1]).setValue(0);
}
That's probably terribly wrong but I feel I ought to at least throw my best guess out before asking for help.

Say, the goal is to process the range C9:D13 by adding the value in D to the value in C, and then setting D to zero. The code would look like this:
function addColumns() {
var sheet = SpreadsheetApp.getActiveSheet(); // or getSheetByName
var range = sheet.getRange("C9:D13");
var values = range.getValues();
for (var i = 0; i < values.length; i++) {
values[i][0] = values[i][0] + values[i][1];
values[i][1] = 0;
}
range.setValues(values);
}
Here, values[i][0] means values the first column in the range we're working on (namely C) and values[i][1] refers to the second. The indexing of JavaScript arrays begins with 0, unlike the numbering of rows in spreadsheets.

Related

Get visible points for a series in LightningChartJs

Exists a function in LightningChartJs to get all visible points from a line or point series in a chart?
If I zoom the chart I want to show something if no visible points available. In some cases I have breaks in my data.
For now I have to check the range and filter all points within this range, but that seems not to be very performant. I guess LC is aware of all the visible points and can give me that.
I would very much welcome any thoughts on the subject or other solutions. Thanks.
LightningChart JS doesn't track the data points that are visible at any time. So the method that you have used to solve the issue is the best way currently.
Something like this seems to be reasonably performant.
function getDataInRange(data, rangeStart, rangeEnd){
const inRangeData = []
const dataLength = data.length
let curPoint
for(let i = 0; i < dataLength; i += 1){
curPoint = data[i]
if(curPoint.x >= rangeStart && curPoint.x <= rangeEnd){
inRangeData.push(curPoint)
}
}
return inRangeData
}
On my personal machine it can process 1 million points in ~10ms ± 2ms. If you only want to know that a point is visible in the range then you could just break the loop as soon as a single point is in the visible range.
Late to the game but for anybody googling:
If you already have a chart defined and it happens to be named 'chart' (otherwise change chart to your chart's object name), you can track the visible start and end data points like this:
axisX = chart.getDefaultAxisX()
window.axisXScaleChangeToken = axisX.onScaleChange((s, e) => {
window.axisXVisibleDataRangeStart = s
window.axisXVisibleDataRangeEnd = e
})
let visiblePoints = [];
for(let i of cur.data){
if(i[0] > window.axisXVisibleDataRangeStart && i[0] < window.axisXVisibleDataRangeEnd) visiblePoints.push(i)
}
Every time the X axis is scaled/zoomed/moved, axisXVisibleDataRangeStart and axisXVisibleDataRangeEnd will change. You're then iterating over where your data points are stored (cur.data in my case and the example) and comparing: If timestamp is within range, push to visiblePoints.
(I am using OHLC where data[0] is the timestamp. Your comparison might be to an object array where {x:} is the value youre looking to compare. You get the idea.)
To remove the listener and stop the logging:
axisX.offScaleChange(window.axisXScaleChangeToken)

Modifying style in JS bypassing current styleSheets.cssRules lmitations with loops on get ID

Firstly, apologies if an answer is somewhere around the internet, but I've searched far and wide and found nothing relevant.
So, as most of you are aware, there are limitations from injecting rules in CSS sheets in certain conditions. Mine is that I cannot use a variable inside a .div:nth-child(thejsvariable) {transform: whatever-transform-option;}. But that extends to any style modifications on using JS variable in the CSS string.
I've tried vaaaarious options and ideas. The most recent one is the one with this problem.
I'm trying to modify any style with a simple getElementById, in which the ID consists of the string "hexpod" and the loop variable.
Now, for the loop variable I need it to select specific items that are on each second row of a grid.
So Tried to nest multiple loops with "multiple of" conditionals.
var wholeRowItems = rowIterations.toFixed(0);
for (var r = 1; r <= hexIterations; r++) {
if (r % 15 == 1 ) {
console.log(r);
for (var t = 1; t <= wholeRowItems; t++) {
console.log(t);
}
}
}
For a bit of Context my grid is fully responsive related to the size of the body.
So the var rowIterations returns how many items with fixed size fit in the body on a row. In my case it's 15 with the toFixed(0) method.
The var hexIterations returns how many items fit in the body. In my case it's 134 items.
Console log returns (not an array, but separate numbers) the range between 1 and 15 followed each time by 16, 31, 46 and so on.
At this point I'm just very frustrated and crying for help on solutions or at least ideas.
You can find the entire code here, if you want to see what's up around the problem, just in case it's relevant.
https://jsitor.com/AXU_pOzBD
Edit: I forgot to mention that once I've placed a getElementById('hexpod' + r + t) it selected only 2 rows, starting in the middle of the rows, one at the beginning and one at about the end aand my brain just faded away.
After more neurons grinding I've managed to find the solution, and I'm post it for the record in case anyone else stumbles over this problem.
I've managed to better understand the conditional logic of multiple of number identification. There should've been "0" instead of "1" in r % 30 statement.
Got the getElementById method to identify the string "hexpod" with r (that is set to target each second row) -15 (in order to start from the second row) + t (to use every grid element of the targeted row).
var wholeRowItems = rowIterations.toFixed(0);
for (var r = 1; r <= hexIterations; r++) {
if (r % 30 === 0 ) {
for (var t = 1; t <= 15; t++) {
var rt = document.getElementById("hexpod" + Number(r - 16 + t));
rt.style.transform = "translateX(-100%) translateY(-75%) rotate(-60deg) skewY(30deg) scale(0.95)";
}
}
}

Script to adjust font-style based on date also overwrites formulas in cells

I'm using a spreadsheet for sheduling, and to keep things clean I'm using a script that will change a rows font style based on the date, so if the date is already passed the font will become italics, greyed out, and have line-through. The script works great, but it overwrites all formulas in the spreadsheet when used. Is there a way to edit the formula so it doesn't affect certain cells? (it should do nothing to rows that are for dates that haven't happened yet).
Here's the script I'm using:
function formatOnDate() {
var sh = SpreadsheetApp.getActive().getActiveSheet();
var range = sh.getDataRange();
var data = range.getValues();
var color = '#AAA';// value you want
var style = 'italic';// value you want
var line = 'line-through';// value you want
var fontColors = range.getFontColors();// get all font colors
var fontLines = range.getFontLines();// lines
var fontStyles = range.getFontStyles();//style
var today = new Date();// include today in sheet
//var today = new Date(new Date().setDate(new Date().getDate()-1));// exclude today... uncomment the one you use
for(var n=1 ; n<data.length ; n++){ // start on row 2 so that headers are not changed
if(data[n][0] < today){
for(var c in data[0]){
fontColors[n][c]=color;//set format
fontLines[n][c]=line;//set format
fontStyles[n][c]=style;//set format
}
}
}
//sh.getRange(1,1,data.length,data[0].length).clear();
// now update sheet with new data and style
sh.getRange(1,1,data.length,data[0].length).setValues(data).setFontColors(fontColors).setFontLines(fontLines).setFontStyles(fontStyles);
}
Is it possible to adjust the font styles without overwriting a formula in a cell? Is it possible to not affect rows for dates that haven't happened yet? If not, is it possible to not affect certain columns? (Column N-Q are the columns that have formulas).
In your script, data is not modified. And also the number of array length of fontColors, fontLines and fontStyles are not modified. So how about this modification?
This modified script doesn't overwrite the data. Only FontColors, FontLines and FontStyles are reflected to range. So formulas of each cell isn't affected.
From :
sh.getRange(1,1,data.length,data[0].length).setValues(data).setFontColors(fontColors).setFontLines(fontLines).setFontStyles(fontStyles);
To :
range.setFontColors(fontColors).setFontLines(fontLines).setFontStyles(fontStyles);
If I misunderstand your question, I'm sorry.

Sorting with constraint no consecutive equals

I would like to sort a news feed according to created date, which is trivial, but I don't want 2 consecutive posts with the same userId field.
This might not be theoritically possible because what if I have only 2 posts with the same userId field?
I am looking for an algorithm that sorts according to fieldA but doesn't have 2 consecutive elements with the same fieldB.
It would also nice to have a parametrized algorithm about the required number of different posts between same user's different posts. (In the first scenario this parameter is 1)
I'm not looking for performance (O(n^2) would be okay) but a clever & simple way, maybe with 5 lines of code.
Language doesn't matter but Javascript is preferred.
To solve this problem in 5 lines is somewhat difficult,I'm trying to give a short pseudocode and you may translate it to js.
First we group the input to A[1],A[2],...,A[k].A[i] is a container contains all posts of i-th user,this can be easily done via O(n) scanning.
code:
for i = 1 to k
lastOccurPosition[i] = -intervalLength; //that is the interval length specified by parameter
for i = 1 to k
sort(A[i]);
for i = 1 to n
minElement = INF; //find the minimum
minUserId = -1; //record whose post is minimun
for j = 1 to k
if(i - lastOccurPosition[i] <= intervalLength) //if the user has occured within interval length,the user's post shouldn't be choosen
continue;
if(A[j][1] < minElement)
minElement = A[j][1];
minUserId = j;
answer[i] = minElement; //put min element into answer array
lastOccurPosition[minUserId] = i; //update choosen user's last occur position
A[minUserId].pop_front(); //delele first element
It is easy to analyse this algorithm's complexity is O(n^2) and I haven't thought out a more concise solution.
Hope to be helpful.
Put the atributes in an array, and sort that array:
arr.sort();
Put the second atribute in another array and sort that array according to the first one.
var newarr = [arr[0]];
for (var i=1; i<arr.length; i++) {
if (arr[i]!=arr[i-1]) newarr.push(arr[i]);
}
Now this just remove duplicates.
This all feels kind of trivial, am I missing something?
Hope this helps.
Cheers,
Gijs

Generate a random array in Javascript/jquery for Sudoku puzzle

I want to fill the 9 x 9 grid from the array by taking care of following condition
A particular number should not be repeated across the same column.
A particular number should not be repeated across the same row.
When i execute the below mentioned code it fills all the 9 X 9 grid with random values without the above mentioned condition.How can I add those two condition before inserting values into my 9 X 9 Grid.
var sudoku_array = ['1','2','3','4','6','5','7','8','9'];
$('.smallbox input').each(function(index) {
$(this).val(sudoku_array[Math.floor(Math.random()*sudoku_array.length)]);
});
My JSFIDDLE LINK
Generating and solving Sudokus is actually not as simple as other (wrong) answers might suggest, but it is not rocket science either. Instead of copying and pasting from Wikipedia I'd like to point you to this question.
However, since it is bad practice to just point to external links, I want to justify it by providing you at least with the intuition why naive approaches fail.
If you start generating a Sudoku board by filling some fields with random numbers (thereby taking into account your constraints), you obtain a partially filled board. Completing it is then equivalent to solving a Sudoku which is nothing else than completing a partially filled board by adhering to the Sudoku rules. If you ever tried it, you will know that this is not possible if you decide on the next number by chosing a valid number only with respect to the 3x3 box, the column and the row. For all but the simplest Sudokus there is some trial and error, so you need a form of backtracking.
I hope this helps.
To ensure that no number is repeated on a row, you might need a shuffling function. For columns, you'll just have to do it the hard way (checking previous solutions to see if a number exists on that column). I hope i am not confusing rows for columns, i tend to do it a lot.
It's similar to the eight queens problem in evolutionary computing. Backtracking, a pure random walk or an evolved solution would solve the problem.
This code will take a while, but it'll do the job.
You can the iterate through the returned two dimensional array, and fill the sudoku box. Holla if you need any help with that
Array.prototype.shuffle = function() {
var arr = this.valueOf();
var ret = [];
while (ret.length < arr.length) {
var x = arr[Math.floor(Number(Math.random() * arr.length))];
if (!(ret.indexOf(x) >= 0)) ret.push(x);
}
return ret;
}
function getSudoku() {
var sudoku = [];
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
sudoku.push(arr);
for (var i = 1; i < 9; i++) {
while (sudoku.length <= i) {
var newarr = arr.shuffle();
var b = false;
for (var j = 0; j < arr.length; j++) {
for (var k = 0; k < i; k++) {
if (sudoku[k].indexOf(newarr[j]) == j) b = true;
}
}
if (!b) {
sudoku.push(newarr);
document.body.innerHTML += `${newarr}<br/>`;
}
}
}
return sudoku;
}
getSudoku()
You need to keep track of what you have inserted before, for the following line:
$(this).val(sudoku_array[Math.floor(Math.random()*sudoku_array.length)]);
For example you can have a jagged array (arrays of arrays, its like a 2-D array) instead of 'sudoku_array' you have created to keep track of available numbers. In fact, you can create two jagged arrays, one for column and one for rows. Since you don't keep track of what you have inserted before, numbers are generated randomly.
After you create an array that keeps available numbers, you do the following:
After you generate number, remove it from the jagged array's respective row and column to mark it unavailable for those row and columns.
Before creating any number, check if it is available in the jagged array(check for both column and row). If not available, make it try another number.
Note: You can reduce the limits of random number you generate to available numbers. If you do that the random number x you generate would mean xth available number for that cell. That way you would not get a number that is not available and thus it works significantly faster.
Edit: As Lex82 pointed out in the comments and in his answer, you will also need a backtracking to avoid dead ends or you need to go deeper in mathematics. I'm just going to keep my answer in case it gives you an idea.

Categories