How to shuffle every second column with google apps script? - google-apps-script

I want to randomize D:E, F:G, H:I, J:K if C is 4 with google apps script.
At the moment I use this inefficient & time-consuming code:
function shuffleAnswers() {
var arr = [0, 2, 4, 6];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,4,1,2);
var column = 4 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
arr = [-2, 0, 2, 4];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,6,1,2);
var column = 6 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
arr = [-4, -2, 0, 2];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,8,1,2);
var column = 8 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
arr = [-6, -4, -2, 0];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,10,1,2);
var column = 10 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
}
Do you have an idea? Maybe with range.randomize()? Every row with C=4 should be randomized. The columns for multiple rows should not be changed to the same position.

Why so slow?
Use of getValues inside the loop slows down the script considerably. Batching up operations is important.
Script Flow:
Create a random array of numbers between 1 to 8 like, [[1,2,5,6,3,4,7,8]] and then use those numbers as indexes for the new row.
Get all values from the sheet and rearrange only rows where C=4 and set back all values in one shot.
Sample script:
/* Create a Random arrray of numbers from 1 to 8 with couples
* eg:[1,2,5,6,3,4,7,8]*/
const doubleShuffleFix = (n = 4) => {
const generator = function*() {//TODO: boilerplate- can be avoided
let i = 1;
yield i;
while (true) {
if (i < (n - 1) * 2) {
yield (i += 2);
} else {
break;
}
}
};
const available = [...generator()];
//Durstenfeld algo
for (let i = available.length - 1; i > 0; i--) {
let rand = Math.floor(Math.random() * i);
[available[i], available[rand]] = [available[rand], available[i]];
}
return available.map(num => [num, ++num]).flat();
};
function shuffleAnswer() {
const s = SpreadsheetApp.getActive().getSheetByName('Sheet1'),
rg = s.getRange(2, 3, s.getLastRow() - 1, 9),
values = rg.getValues();
rg.setValues(
values.map(row => {
if (row[0] === 4) {
return [4, ...doubleShuffleFix().map(num => row[num])];
}
return row;
})
);
}
References:
Best practices
Durstenfeld algorithm

Related

Exception: The number of rows in the data does not match the number of rows in the range. The data has 1 but the range has 8. Google Script

I am having troubles in
Exception: The number of rows in the data does not match the number of rows in the range. The data has 1 but the range has 8. Google Script
function myFunction() {
var s1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Staging');
var s2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var values1 = s1.getDataRange().getValues();
var values2 = s2.getDataRange().getValues();
var Avals = s2.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
var resultArray = [];
for(var n=0; n < values1.length ; n++)
{
var keep = false;
var counter = 0;
for(var p=0; p < values2.length ; p++)
{
if( values1[n][1] == values2[p][1])
{
keep = true;
break ;
}
}
if(keep == false)
{
resultArray.push([values1[n]]);
//s2.appendRow( values1[n] );
s2.getRange(Alast + 1, 1, values1[n].length, values1[n].length).setValues([values1[n]]);
//s2.getRange(s2.getLastRow() + 1, 1, values1.length, values1[0].length).appendRow( values1[n] );
resultArray = [];
keep = false
}
}
}
I believe you are trying to keep appending a new row to the end of the data on "Final". You should revise you script as shown below. Use resultArray to collect all new rows and save it to spreadsheet once.
function myFunction() {
var s1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Staging');
var s2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var values1 = s1.getDataRange().getValues();
var values2 = s2.getDataRange().getValues();
// s2.getDataRange() will get up to the last row of data
var Alast = values2.length;
var resultArray = [];
for(var n=0; n < values1.length ; n++)
{
var keep = false;
var counter = 0;
for(var p=0; p < values2.length ; p++)
{
if( values1[n][1] == values2[p][1])
{
keep = true;
break ;
}
}
if(keep == false)
{
resultArray.push(values1[n]);
keep = false
}
}
if( resultArray.length > 0 ) {
s2.getRange(Alast+1,1,resultArray.length,resultArray[0].length).setValues(resultArray);
}
}

Is there a way to use a random number of randomly generated numbers as variables? [duplicate]

I want to randomize D:E, F:G, H:I, J:K if C is 4 with google apps script.
At the moment I use this inefficient & time-consuming code:
function shuffleAnswers() {
var arr = [0, 2, 4, 6];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,4,1,2);
var column = 4 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
arr = [-2, 0, 2, 4];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,6,1,2);
var column = 6 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
arr = [-4, -2, 0, 2];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,8,1,2);
var column = 8 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
arr = [-6, -4, -2, 0];
for (var i = 2; i < lastRow()+1; i++)
{
var amount = sheet().getRange(i,3).getValue();
if (amount == 4)
{
var source = sheet().getRange(i,10,1,2);
var column = 10 + arr[(Math.random() * arr.length) | 0];
var destination = sheet().getRange(i,column,1,2);
var valuesSource = source.getValues();
var valuesDestination = destination.getValues();
source.setValues(valuesDestination);
destination.setValues(valuesSource);
}
}
}
Do you have an idea? Maybe with range.randomize()? Every row with C=4 should be randomized. The columns for multiple rows should not be changed to the same position.
Why so slow?
Use of getValues inside the loop slows down the script considerably. Batching up operations is important.
Script Flow:
Create a random array of numbers between 1 to 8 like, [[1,2,5,6,3,4,7,8]] and then use those numbers as indexes for the new row.
Get all values from the sheet and rearrange only rows where C=4 and set back all values in one shot.
Sample script:
/* Create a Random arrray of numbers from 1 to 8 with couples
* eg:[1,2,5,6,3,4,7,8]*/
const doubleShuffleFix = (n = 4) => {
const generator = function*() {//TODO: boilerplate- can be avoided
let i = 1;
yield i;
while (true) {
if (i < (n - 1) * 2) {
yield (i += 2);
} else {
break;
}
}
};
const available = [...generator()];
//Durstenfeld algo
for (let i = available.length - 1; i > 0; i--) {
let rand = Math.floor(Math.random() * i);
[available[i], available[rand]] = [available[rand], available[i]];
}
return available.map(num => [num, ++num]).flat();
};
function shuffleAnswer() {
const s = SpreadsheetApp.getActive().getSheetByName('Sheet1'),
rg = s.getRange(2, 3, s.getLastRow() - 1, 9),
values = rg.getValues();
rg.setValues(
values.map(row => {
if (row[0] === 4) {
return [4, ...doubleShuffleFix().map(num => row[num])];
}
return row;
})
);
}
References:
Best practices
Durstenfeld algorithm

Search for values in arrays

I've a spreadsheet with two sheets (Foglio1, Foglio2)
Foglio1 has five rows of numeric values in two columns, Foglio2 2 has five rows of numeric values in the first column.
I'd like to search for the Foglio2 values in Foglio1 and if it finds something copy the respective value of Foglio1, column2 into Foglio2, column2.
This is what I've got so far but it doesn't seems to work, basically it doesn't find anything so the result array remains empty
function cercaCopia() {
var ss = SpreadsheetApp.openById('myfileIDhere');
var data1 = ss.getSheetByName('Foglio2').getRange(1, 1, 5).getValues();
var data2 = ss.getSheetByName('Foglio1').getRange(1, 1, 5, 2).getValues();
var result = new Array(4);
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 5; j++) {
if (data1[i] == data2[j, 0]) {
result[i] = data2[j, 1]
}
}
}
}
This works:
function myFunction() {
var ss = SpreadsheetApp.openById('sheetid');
var data1 = ss.getSheetByName('Foglio2').getRange(1, 1, 5).getValues();
var data2 = ss.getSheetByName('Foglio1').getRange(1, 1, 5, 2).getValues();
for (var j=0; j<5;j++){
for (var i = 0; i < 5; i++) {
if (data1[j][0]==data2[i][0]){
var range= ss.getSheetByName('Foglio2').getRange(1,1,5,5);
range.getCell(j+1,2).setValue(data2[i][1]);
}
}
}
}
I get this as a result:
In Foglio1 I had this:

How to Get (& Set) cell values from one sheet to another in Google Sheets?

Looking to get values from several cells in the first sheet (POTemplate) of my Google Sheet file that serves as an order entry form. Line 22 is the first line in the form that collects data from our user regarding items requested for order.
The tab to which I'd like to set these values (POHistory) will serve as a running log of all order details keyed into the POTemplate tab with each order. Each entry recorded in the PO template log should include each orders' unique PO No. (found in cell N3 of the POTemplate tab) & PO Date (cell N4 of the POTemplate tab). I sincerely appreciate the help.
function submit() {
var app = SpreadsheetApp;
var tplSheet = app.getActiveSpreadsheet().getSheetByName("POTemplate");
var tplFRow = 22, tplLRow = tplSheet.getLastRow();
var tplRowsNum = tplLRow - tplFRow + 1;
var tplFCol = 1, tplLCol = 16;
var tplColsNum = tplLCol - tplFCol + 1;
var rangeData = tplSheet.getRange(22, 1, tplRowsNum, tplColsNum).getValues();
var colIndexes = [0, 3, 10, 12, 15];
var fData = filterByIndexes(rangeData, colIndexes);
var target = "POHistory";
var targetSheet = app.getActiveSpreadsheet().getSheetByName(target);
var tgtRow = targetSheet.getLastRow() + 1;
var tgtRowsNum = fData.length - tgtRow + 1;
var tgtCol = 1;
var tgtColsNum = fData[0].length - 1 + 1;
targetSheet.getRange(tgtRow, tgtCol, tgtRowsNum,
tgtColsNum).setValues(fData);
}
function filterByIndexes(twoDArr, indexArr) {
var fData = [];
twoDArr = twoDArr.transpose();
for(var i=0; i<indexArr.length; i++) {
fData.push(twoDArr[indexArr[i]]);
}
return fData.transpose();
}
Array.prototype.transpose = function() {
var a = this,
w = a.length ? a.length : 0,
h = a[0] instanceof Array ? a[0].length : 0;
if (h === 0 || w === 0) {return [];}
var i, j, t = [];
for (i = 0; i < h; i++) {
t[i] = [];
for (j = 0; j < w; j++) {
t[i][j] = a[j][i];
}
}
return t;
}
I can offer you a modification of the submit() function you have given.
function process(){
submit();
submitDate();
}
function submitDate() {
var app = SpreadsheetApp;
var tplSheet = app.getActiveSpreadsheet().getSheetByName("POTemplate");
var tplFRow = 1, tplLRow = 21;
var tplRowsNum = tplLRow - tplFRow + 1;
var tplFCol = 1, tplLCol = 16;
var tplColsNum = tplLCol - tplFCol + 1;
var rangeData = tplSheet.getRange(tplFRow, tplFCol, tplLRow, tplColsNum).getValues();
var colIndexes = [13];
var fData = filterByIndexes(rangeData, colIndexes);
var target = "POHistory";
var targetSheet = app.getActiveSpreadsheet().getSheetByName(target);
var tgtRow = targetSheet.getLastRow() + 1;
var tgtRowsNum = fData.length - tgtRow + 1;
var tgtCol = 1;
var tgtColsNum = fData[0].length - 1 + 1;
Logger.log(fData)
fData=fData.transpose();
Logger.log(fData)
targetSheet.getRange(tgtRow-1, 5, fData.length,
fData[0].length).setValues(fData);
}
function submit() {
var app = SpreadsheetApp;
var tplSheet = app.getActiveSpreadsheet().getSheetByName("POTemplate");
var tplFRow = 22, tplLRow = tplSheet.getLastRow();
var tplRowsNum = tplLRow - tplFRow + 1;
var tplFCol = 1, tplLCol = 16;
var tplColsNum = tplLCol - tplFCol + 1;
var rangeData = tplSheet.getRange(22, 1, tplRowsNum, tplColsNum).getValues();
var colIndexes = [0, 3, 10, 12, 15];
var fData = filterByIndexes(rangeData, colIndexes);
var target = "POHistory";
var targetSheet = app.getActiveSpreadsheet().getSheetByName(target);
var tgtRow = targetSheet.getLastRow() + 1;
var tgtRowsNum = fData.length - tgtRow + 1;
var tgtCol = 1;
var tgtColsNum = fData[0].length - 1 + 1;
targetSheet.getRange(tgtRow, tgtCol, tgtRowsNum,
tgtColsNum).setValues(fData);
}
function filterByIndexes(twoDArr, indexArr) {
var fData = [];
twoDArr = twoDArr.transpose();
for(var i=0; i<indexArr.length; i++) {
fData.push(twoDArr[indexArr[i]]);
}
return fData.transpose();
}
Array.prototype.transpose = function() {
var a = this,
w = a.length ? a.length : 0,
h = a[0] instanceof Array ? a[0].length : 0;
if (h === 0 || w === 0) {return [];}
var i, j, t = [];
for (i = 0; i < h; i++) {
t[i] = [];
for (j = 0; j < w; j++) {
t[i][j] = a[j][i];
}
}
return t;
}
Basically, similar code runs twice. I could be criticized for doing this, but perhaps it is convenient for you this way.

Losing importXML functions when running my current script

I am writing a Google script to copy values from a column of importXML data based on a certain date. Below is where I am currently, but the issue I am running into now is when the script is run it is removing the importXML formulas located in columnns G and H:
function moveValuesOnly2() {
var sheet = SpreadsheetApp.openById("0At2fAZApHI8adHJxWU5EVVBEbHd0YzA5UVBwOGQ3ZHc");
SpreadsheetApp.setActiveSpreadsheet(sheet);
SpreadsheetApp.setActiveSheet(sheet.getSheetByName('Dylan'))
var range= sheet.getRange("B3:AJ50");
var currentValue = range.getValues();
var COL_B = 0; // relative to col B
var COL_G = 5;
var COL_H = 6;
var COL_I = 7;
var COL_J = 8;
var COL_K = 9;
var COL_L = 10;
var COL_N = 12;
var COL_O = 13;
var COL_P = 14;
var COL_R = 16;
var COL_S = 17;
var COL_T = 18;
var COL_V = 20;
var COL_W = 21;
var COL_X = 22;
var COL_Z = 24;
var COL_AA = 25;
var COL_AB = 26;
var COL_AD = 28;
var COL_AE = 29;
var COL_AF = 30;
var COL_AH = 32;
var COL_AI = 33;
var COL_AJ = 34;
for(var i = 0 ; i < currentValue.length ; i++){
if (currentValue[i][0] == (1)) {
// Copy value at 0 days
currentValue[i][COL_J] = currentValue[i][COL_G];
currentValue[i][COL_K] = currentValue[i][COL_H];
currentValue[i][COL_L] = currentValue[i][COL_I];
}
for(var i = 0 ; i < currentValue.length ; i++){
if (currentValue[i][0] == (15)) {
// Copy value at 15 days
currentValue[i][COL_N] = currentValue[i][COL_G];
currentValue[i][COL_O] = currentValue[i][COL_H];
currentValue[i][COL_P] = currentValue[i][COL_I];
}
range.setValues(currentValue);
}
for(var i = 0 ; i < currentValue.length ; i++){
if (currentValue[i][0] == (30)) {
// Copy value at 30 days
currentValue[i][COL_R] = currentValue[i][COL_G];
currentValue[i][COL_S] = currentValue[i][COL_H];
currentValue[i][COL_T] = currentValue[i][COL_I];
}
range.setValues(currentValue);
}
for(var i = 0 ; i < currentValue.length ; i++){
if (currentValue[i][0] == (45)) {
// Copy value at 45 days
currentValue[i][COL_V] = currentValue[i][COL_G];
currentValue[i][COL_W] = currentValue[i][COL_H];
currentValue[i][COL_X] = currentValue[i][COL_I];
}
range.setValues(currentValue);
}
for(var i = 0 ; i < currentValue.length ; i++){
if (currentValue[i][0] == (60)) {
// Copy value at 60 days
currentValue[i][COL_Z] = currentValue[i][COL_G];
currentValue[i][COL_AA] = currentValue[i][COL_H];
currentValue[i][COL_AB] = currentValue[i][COL_I];
}
range.setValues(currentValue);
}
for(var i = 0 ; i < currentValue.length ; i++){
if (currentValue[i][0] == (75)) {
// Copy value at 75 days
currentValue[i][COL_AD] = currentValue[i][COL_G];
currentValue[i][COL_AE] = currentValue[i][COL_H];
currentValue[i][COL_AF] = currentValue[i][COL_I];
}
range.setValues(currentValue);
}
for(var i = 0 ; i < currentValue.length ; i++){
if (currentValue[i][0] == (90)) {
// Copy value at 90 days
currentValue[i][COL_AH] = currentValue[i][COL_G];
currentValue[i][COL_AI] = currentValue[i][COL_H];
currentValue[i][COL_AJ] = currentValue[i][COL_I];
}
range.setValues(currentValue);
}}}
Is there any way to prevent the ImportXML functions located in those columns from being lost each time the script is run. Also a huge thank you to user #Srik to getting to me to the point I am at!
I would think that the problem lies from the fact that you setValues on the range which includes your columns G & H.
The solution lies in using
var specialRange = sheet.getRange("G3:H50");
var formulas = specialRange.getFormulas();
at the begining of your script to save the formulas and then after your setValues call to use the following code
specialRange.setFormulas(formulas);
Let me know how that works for you.