Navigating the columns and rows in pasting array values - javascript

I'm trying to get the list of names from the Column A, randomize the list, and then distribute them evenly (as much as possible, including the remainders from division) by the user-specified number of groups.
An example of what I need is like this:
List of Names: A, B, C, D, E, F, G, H, I, J, K
Result of 3 Groups:
Group 1: D, A, F
Group 2: B, H, G, K
Group 3: E, C, I, J
Edited: I've cleaned up the code: I have assigned the list of names to an empty array and randomized the array successfully. Now I've got to figure out how to paste these values in their own columns for each groups. How do I paste the values to the right and down each column also accounting for the remainders (the first values are the headers for each columns):
Column C: Group 1, D, A, F
Column D: Group 2, B, H, G, K
Column E: Group 3, E, C, I, J
This is what I have so far:
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Custom Menu')
.addItem('Show prompt', 'showPrompt')
.addToUi();
}
function SortNames() {
var ui = SpreadsheetApp.getUi();
var result = ui.prompt(
'How many groups?',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var groupquantity = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK" - Need to clear the cells from the previous sorting in this step
// Get the last row number of the names list
var Avals = SpreadsheetApp.getActiveSheet().getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
// Set an empty Array
var ar = [];
/****** In its original order, append the names to the array *****/
for (var i = 2; i < Alast+1; i++) {
var source = 'A' + i;
var Avals = SpreadsheetApp.getActiveSheet().getRange(source).getValues();
ar.push(Avals);
}
/***************************/
/****** Shuffle the array *****/
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i--) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
}
shuffle(ar);
/***************************/
/****** Calculates the rounded down # of members per group *****/
var memberspergroup = ar.length / groupquantity;
var memberspergroup = Math.floor(memberspergroup);
/*********************************/
/****** Copy and Paste the rounded down number of members to each groups until
the remainder is 0, then distribute evenly with remaining number of groups *****/
// First Cell location to paste
var pasteloc = "C1";
for (var i = 1; i <= groupquantity; i++) {
SpreadsheetApp.getActiveSheet().getRange(pasteloc).setValue('Group ' + i);
var source = 'A' + i;
var Avals = SpreadsheetApp.getActiveSheet().getRange(source).getValues();
}
/*********************************/
}
else if (button == ui.Button.CANCEL) {
// User clicked "Cancel".
ui.alert('The request has been cancelled');
}
else if (button == ui.Button.CLOSE) {
// User clicked X in the title bar.
ui.alert('You closed the dialog.');
}
}

I have found the solution to my question. It is not in the best shape - it can use an improvement to account for empty cells in the names list. However, it functions properly, and does everything I was looking for:
It assigns the list of names in an array
Randomizes the array
Distributes completely evenly which takes account for remainders (Just like the example I provided above)
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Custom Menu')
.addItem('Show prompt', 'showPrompt')
.addToUi();
/******closing function onOpen()*********************/
}
function SortNames() {
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var result = ui.prompt(
'How many groups?',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var groupquantity = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK"
// Get the last row number of the names list
var Avalues = sheet.getRange("A1:A").getValues();
var Alast = Avalues.filter(String).length;
if(groupquantity > 0 && groupquantity <= Alast)
{
// User inputted a valid group quantity - Need to clear the cells from the previous sorting in this step
var lastRow = SpreadsheetApp.getActiveSheet().getMaxRows();
sheet.getRange('C1:Z'+lastRow).clearContent();
// Set an empty Array
var ar = [];
/****** In its original order, append the names to the array *****/
for (var i = 2; i < Alast+1; i++) {
var source = 'A' + i;
var Avals = sheet.getRange(source).getValues();
ar.push(Avals);
}
/***************************/
/****** Shuffles array *****/
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i--) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
/**********closing function shuffle(a)*****************/
}
shuffle(ar);
/****** Calculates the rounded down # of members per group *****/
var memberspergroup = Math.floor(ar.length / groupquantity);
/*********************************/
/****** Main Goal: Copy and Paste the rounded down number of members to each groups until
the remainder is 0, then distribute evenly with remaining number of groups *****/
// 1. Define the first Cell location to paste
var pasteloc = "C1";
// 2. Begin a for-loop: Navigate Horizontally across from the first cell location
for (var i = 1; i <= groupquantity; i++)
{
// 3. Set the Column Headings in the current column, i
sheet.getRange(1,i+2).setValue('Group ' + i);
/************** 4. Fill in the Rows of names for each groups **********/
// List out the values in array "ar" by the expected group qty, until the remainder is zero
if ((ar.length)%(groupquantity-(i-1)) > 0)
{
for (var rows = 2; rows <= memberspergroup+1; rows++)
{
var j = 0;
sheet.getRange(rows,i+2).setValue(ar[j]);
var index = ar.indexOf(ar[j]);
ar.splice(index, 1);
}
}
else
{
var memberspergroup = ar.length/(groupquantity-(i-1))
for (var rows = 2; rows <= memberspergroup+1; rows++)
{
var j = 0;
sheet.getRange(rows,i+2).setValue(ar[j]);
var index = ar.indexOf(ar[j]);
ar.splice(index, 1);
}
}
}
/*********************************/
}
/*****************closing if(groupquantity > 0 && groupquantity <= Alast)****************/
else{
ui.alert("Error: " + '"' + groupquantity + '"' +" is not a proper group quantity")
}
}
else if (button == ui.Button.CANCEL) {
// User clicked "Cancel".
}
else if (button == ui.Button.CLOSE) {
// User clicked X in the title bar.
}
}
I hope this helps!

Related

Google Script: Multiple conditions per unique ID

The script would count the number of times all VIN numbers are repeated & if all parts have arrived for that VIN Number (Car).
In example, if the VIN number is repeated 5 times then that means there are five parts going to arrive, so then the next step would be to check the arrived column for such VIN if there are 5 "Yes" values then
(VIN number repeated) 5/5 (Number of "Yes" values)
would trigger it to change the [Master] tab Parts Order column to "Yes" for that particular VIN number.
User would manually update the [Parts] tab, arrived column with either "Yes" or leave blank. (If blank then part has not arrived.)
See link for google sheet Template:
https://docs.google.com/spreadsheets/d/1wlGV_QCWpRwmI5FWiOli6lXuzRATy_Goygp3lhtm-Ek/edit?usp=sharing
My attempt:
Special function to get the last value of a column:
function getLastRowSpecial(range){
var rowNum = 0;
var blank = false;
for(var row = 0; row < range.length; row++){
if(range[row][0] === "" && !blank){
rowNum = row;
blank = true;
}else if(range[row][0] !== ""){
blank = false;
};
};
return rowNum;
};
Here I was able to count the number of times each VIN Number appears, but I was unable to count the number of "Yes" values for each unique VIN number. This needs to be dynamic. My approach at the end was not dynamic. Regarding in particular the, var number
Main Script:
/** ---------------------- SPREAD SHEETS ---------------------- **/
var masterS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Master");
var partS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Parts");
/** ----------------- Selecting Range of Cells ----------------- **/
/** ----- Parts Spread Sheet ----- **/
/** VIN to Match */
var vinPartOrderRangeP = partS.getRange("C5:C");
var vinPartValuesP = vinPartOrderRangeP.getValues();
var columnCheckPartVINP = getLastRowSpecial(vinPartValuesP);
var partVINDataRangeP = partS.getRange(5, 3, columnCheckPartVINP, 1);
var partsVinSetP = partVINDataRangeP.getValues();
/** Part Arrived */
var partOrderedRangeP = partS.getRange("N5:N");
var partOrderedValuesP = partOrderedRangeP.getValues();
var partOrderedValuesCorrectLengthP = partOrderedValuesP.splice(0,partsVinSetP.length);
/** Combining VINs with Parts Arrived */
var vinPartsArrivedP = [];
vinPartsArrivedP.push(partsVinSetP,partOrderedValuesCorrectLengthP);
/** ----- Master Spread Sheet ----- **/
/** VIN to Match */
var vinPartOrderRangeM = masterS.getRange("B5:B");
var vinPartValuesM = vinPartOrderRangeM.getValues();
var columnCheckPartVINM = getLastRowSpecial(vinPartValuesM);
var partVINDataRangeM = masterS.getRange(5, 2, columnCheckPartVINM, 1);
var partsVinSetM = partVINDataRangeM.getValues();
/** Part Arrived */
var partPastRangeM = masterS.getRange("I5:I");
var partPastValuesM = partPastRangeM.getValues();
/** ---- For-Loop getting Number of Parts that need to Arrive ---- **/
var vinNumber = [], arrivalPartsRequired = [], prev;
for (var i = 0; i < vinPartsArrivedP[0].length; i++) {
if (vinPartsArrivedP[0][i][0] !== prev) {
vinNumber.push(vinPartsArrivedP[0][i][0]);
arrivalPartsRequired.push(1);
} else {
arrivalPartsRequired[arrivalPartsRequired.length - 1]++;
}
prev = vinPartsArrivedP[0][i][0];
}
console.log('[' + vinNumber[0] + ']','[' + arrivalPartsRequired[0] + ']')
/**
* Now we can say arrivalPartsRequired has the number of Yes's we need
* per each VIN number.
**/
console.log(vinPartsArrivedP[0][3][0])
var number = 0;
var number2 = 0;
var number3 = 0;
var number4 = 0;
var number5 = 0;
for (var j = 0; j < partsVinSetM.length; j++) {
};
for (var k=0; k<vinPartsArrivedP[0].length; k++){
if(vinNumber[0] == vinPartsArrivedP[0][k][0]){
number++
for (var i=0; i<partOrderedValuesP[0].length; i++){
for (var j = 0; j < partOrderedValuesP[0].length; j++) {
if (partOrderedValuesP[i][j] == 'Yes') {
console.log(i);
return i+1;
}
}
return -1;
}
}
if(vinNumber[1] == vinPartsArrivedP[0][k][0]){
number2++
}
if(vinNumber[2] == vinPartsArrivedP[0][k][0]){
number3++
}
if(vinNumber[3] == vinPartsArrivedP[0][k][0]){
number4++
}
if(vinNumber[4] == vinPartsArrivedP[0][k][0]){
number5++
}
};
console.log(number);
console.log(number2);
console.log(number3);
console.log(number4);
console.log(number5);
I believe your goal as follows.
You want to retrieve the values from the columns "C" and "N" from "Parts" sheet, and want to check whether the number of same VIN # at the column "C" and the number of yes at the column "N".
When both numbers are the same, you want to put yes to the column "I" of "Master" sheet.
You want to achieve this using Google Apps Script.
In this case, in order to achieve your goal, how about the following flow?
Retrieve values from "Parts" sheet.
Create an object for checking the number of VIN # and yes.
Create an array for putting to the "Master" sheet.
Put the values to the "Master" sheet.
When this flow is reflected to a sample script, it becomes as follows.
Sample script:
function sample() {
// 1. Retrieve values from "Master" sheet.
var masterS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Master");
var valuesOfParts = partS.getRange("C5:N" + partS.getLastRow()).getValues();
// 2. Create an object for checking the number of `VIN #` and `yes`.
var obj = valuesOfParts.reduce((o, [c,,,,,,,,,,,n]) => {
if (o[c]) {
o[c].c += 1;
if (n == "yes") o[c].yes += 1;
} else {
o[c] = {c: 1, yes: n == "yes" ? 1 : 0};
}
return o;
}, {});
// 3. Create an array for putting to the "Master" sheet.
var partS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Parts");
var rangeOfMaster = masterS.getRange("B5:B" + masterS.getLastRow());
var valuesOfMaster = rangeOfMaster.getValues().map(([b]) => [obj[b] && obj[b].c == obj[b].yes ? "yes" : ""]);
// 4. Put the values to the "Master" sheet.
rangeOfMaster.offset(0, 7).setValues(valuesOfMaster);
}
References:
reduce()
map()

Google Script working as 2 separate scripts but not inside the same function

Basically I have a script that is in 4 blocks:
1. Copies within a range each row provided it meets a criteria
2. Removes all empty rows
3. Sets all numbers as percentage
4. Applies conditional cell formatting to one of the columns
The 4th part is the one that is causing me issues. The script runs without any error message AND block 4 works perfectly fine if it's in another script alone with the same variables defined but as soon as it is inside the same function as the others it simply doesn't run without any error message of any kind.
Tried changing the name of the variables to single use ones to ensure it wasn't because one of the "var" was modified above it, removing the "else if" to keep only an "if" in the loop, moving it around to other parts of the script but if the block 1 is in the script then block 4 won't apply (will apply if it is only with 2 & 3.
2 & 3 which follow the same structure work well with 1.
Does any one have any clue what's wrong with my script ? :)
Each block is commented with what it does
function copy() {
//Set variables & criterion to choose which rows to copy
var s = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1bEiLWsbFszcsz0tlQudMBgTk5uviyv_wDx7fFa8txFM/edit');
var ssSource = s.getSheetByName('Variations');
var ssDest = s.getSheetByName('Email');
var lastRowSource = ssSource.getLastRow();
var lastRowDest = ssDest.getLastRow();
var lastColSource = ssSource.getLastColumn()
var criteria = 0;
var titles = ssSource.getRange(1,1,1, lastColSource).getValues()
//Copies the range
ssDest.getRange(1,1,1, lastColSource).setValues(titles)
for (var i = 2; i < lastRowSource; i++ ) {
var test = ssSource.getRange(i ,1);
Logger.log(test.getValue()+ ' <? ' + criteria);
if (ssSource.getRange(i ,6).getValue() > criteria) {
ssSource.getRange(i ,1,1,ssSource.getLastColumn()).copyTo(ssDest.getRange(i ,1,1,ssSource.getLastColumn()), {contentsOnly:true}); // copy/paste content only
}
}
//Removes empty rows
var data = ssDest.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n].join().replace(/,/g,'')!=''){ targetData.push(data[n])};
Logger.log(data[n].join().replace(/,/g,''))
}
ssDest.getDataRange().clear();
ssDest.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
//Formats numbers as percentages
var rangePercent = ssDest.getRange(1,1,ssDest.getLastRow(),ssDest.getLastColumn());
var rowsPercent = rangePercent.getNumRows();
var colsPercent = rangePercent.getNumColumns();
for(var rowPercent = 1; rowPercent <= rowsPercent; rowPercent++) {
for(var colPercent = 1; colPercent <= colsPercent; colPercent++) {
var cellPercent = rangePercent.getCell(rowPercent, colPercent);
var valuePercent = cellPercent.getValue();
if(typeof(valuePercent) == 'number') {
cellPercent.setNumberFormat("##.#%");
}
}
}
//Adds conditional background colours
for (var z = 2; z < lastRowDest+1;z++) {
var avgCpc = 4;
var rangeColour = ssDest.getRange(z,avgCpc);
var dataColour = rangeColour.getValue()
if (dataColour < 0) {
ssDest.getRange(z,avgCpc).setBackground('#d9ead3')
}
else if (dataColour > 0) {
ssDest.getRange(z,avgCpc).setBackground('#f4cccc')
}
}
//Centers Values
}
The problem you're having is your code has performance issues because you're calling too many times methods such as getRange() and getValue() inside various loops, therefore Apps Script can't keep up with all those calls. Please check Best Practices.
Having said that, I modified your code in order to make it more efficient. Besides your copy function, I added two more functions to make the code more readable.
function copy
As before this function sets the variables, but now it calls two other functions, which are setPositiveCostValues and formatCells
function copy() {
//Set variables & criterion to choose which rows to copy
var ss = SpreadsheetApp.openByUrl('your-url');
var ssSource = ss.getSheetByName('Variations');
var ssDest = ss.getSheetByName('Email');
// set the title
var titles = ssSource.getRange(1,1,1, ssSource.getLastColumn()).getValues();
ssDest.getRange(1,1,1, ssSource.getLastColumn()).setValues(titles);
// get the positive values you want from the cost col
var positiveValues = setPositiveCostValues(ssSource, ssDest, ssSource.getLastRow());
// fomrat the cells you want as percentage and set the color
formatCells(ssDest, positiveValues);
}
function setPositiveCostValues
This will take the values where the cost is positive and it will get rip off of the cells with empty values and "n/a" values.
function setPositiveCostValues(ssSource,ssDest, lastRowSource){
var postiveCost = ssSource.getRange(2, 1, lastRowSource, 6).getValues();
// this loop will clean the empty elements and the ones that only have n/a
for (var i = postiveCost.length - 1; i >= 0; i--) {
if (postiveCost[i][0]) {
postiveCost.splice(i + 1, postiveCost.length - (i + 1));
postiveCost = postiveCost.filter(function(el){ return el != 'n/a'})
break;
}
}
return postiveCost;
}
function formatCells
This will format the cells in the cost col as a percentage and will set the right color in your avgCpc col.
function formatCells(ssDest, postiveCost){
var avgCpc = 4, cost = 6, row = 2, criteria = 0;
// iterate over the array and depending on the criteria format the cells
postiveCost.forEach(function(el){
if(el[cost - 1] > criteria){
var ssDestRange = ssDest.getRange(row, 1, 1, cost);
ssDestRange.setValues([el]);
ssDestRange.getCell(1, cost).setNumberFormat("##.#%");
// set the color depending on the avgCpc value condition
if(el[avgCpc - 1] < criteria) ssDest.getRange(row, avgCpc).setBackground('#d9ead3');
else ssDest.getRange(row, avgCpc).setBackground('#f4cccc');
row++;
}
});
}
Code
Your whole code now it will look like this:
function copy() {
//Set variables & criterion to choose which rows to copy
var ss = SpreadsheetApp.openByUrl('your-url');
var ssSource = ss.getSheetByName('Variations');
var ssDest = ss.getSheetByName('Email');
// set the title
var titles = ssSource.getRange(1,1,1, ssSource.getLastColumn()).getValues();
ssDest.getRange(1,1,1, ssSource.getLastColumn()).setValues(titles);
// get the positive values you want from the cost col
var positiveValues = setPositiveCostValues(ssSource, ssDest, ssSource.getLastRow());
// fomrat the cells you want as percentage and set the color
formatCells(ssDest, positiveValues);
}
function setPositiveCostValues(ssSource,ssDest, lastRowSource){
var postiveCost = ssSource.getRange(2, 1, lastRowSource, 6).getValues();
// this loop will clean the empty elements and the ones that only have n/a
for (var i = postiveCost.length - 1; i >= 0; i--) {
if (postiveCost[i][0]) {
postiveCost.splice(i + 1, postiveCost.length - (i + 1));
postiveCost = postiveCost.filter(function(el){ return el != 'n/a'})
break;
}
}
return postiveCost;
}
function formatCells(ssDest, postiveCost){
var avgCpc = 4, cost = 6, row = 2, criteria = 0;
// iterate over the array and depending on the criteria format the cells
postiveCost.forEach(function(el){
if(el[cost - 1] > criteria){
var ssDestRange = ssDest.getRange(row, 1, 1, cost);
ssDestRange.setValues([el]);
ssDestRange.getCell(1, cost).setNumberFormat("##.#%");
// set the color depending on the avgCpc value condition
if(el[avgCpc - 1] < criteria) ssDest.getRange(row, avgCpc).setBackground('#d9ead3');
else ssDest.getRange(row, avgCpc).setBackground('#f4cccc');
row++;
}
});
}

How to automate randomizing 46 names to create 46 x 6 unique rows and columns in Google sheet?

I am working with automation in Google sheet. Can you help me?
This problem is for sending surveys to 46 people. Each people needs to rate 5 people from those 46 people.
Requirements:
1. 1 rater, for 5 uniques ratees
2. No duplicate name per row (it should be 6 unique names in a row)
3. No duplicate name per column (it should be 46 unique names per column)
Expected output is for us to create 46x6 random names with no duplicates in row and columns.
-
-
Flow:
If a unique matrix across and below can be created, then it's values can be used as keys to the actual name array.
Create a 2D number array with length = number of rows
Loop through required number of columns and rows
Create a temporary array (tempCol) to store current column data
Fill the array with random numbers
Use indexOf to figure out if any random numbers are already present in the currentrow/ current column, if so, get a new random number.
In random cases, where it's impossible to fill up the temporary column with unique random numbers across and below, delete the temporary column and redo this iteration.
Snippet:
function getRandUniqMatrix(numCols, numRows) {
var maxIter = 1000; //Worst case number of iterations, after which the loop and tempCol resets
var output = Array.apply(null, Array(numRows)).map(function(_, i) {
return [i++]; //[[0],[1],[2],...]
});
var currRandNum;
var getRandom = function() {
currRandNum = Math.floor(Math.random() * numRows);
}; //get random number within numRows
while (numCols--) {//loop through columns
getRandom();
for (
var row = 0, tempCol = [], iter = 0;
row < numRows;
++row, getRandom()
) {//loop through rows
if (//unique condition check
!~output[row].indexOf(currRandNum) &&
!~tempCol.indexOf(currRandNum)
) {
tempCol.push(currRandNum);
} else {
--row;
++iter;
if (iter > maxIter) {//reset loop
iter = 0;
tempCol = [];
row = -1;
}
}
}
output.forEach(function(e, i) {//push tempCol to output
e.push(tempCol[i]);
});
}
return output;
}
console.info(getRandUniqMatrix(6, 46));
var data1d = data.map(function(e){return e[0]});
var finalArr = getRandUniqMatrix(6, 46).map(function(row){return row.map(function(col){return data1d[col]})});
destSheet.getRange(1,1,finalArr.length, finalArr[0].length).setValues(finalArr);
The OP wants to create a review matrix in which the names of the reviewed employees are chosen at random, the reviewer cannot review themselves, and the matrix is completed for 46 employees.
Based on previous code, this version builds an array of employee names for each row, in which the name of the reviewer is not included in the array. Five names are chosen at random and applied to the reviewer. The loop then repeats through each of the 46 employees.
For example, in the first round of reviews, "name01" is omitted from the array of employees from which the "reviewees" are randomly chosen. In the second round, "name01" is included, but "name02" is excluded from the array of employees. And so on, such that in each case, the array of employees used for the random selection of five reviews is always 45 names in length, and excludes the name of the reviewer.
The random selection of names to be rated does not ensure an equal and even distribution of reviews among employees. Though each employee will conduct 5 reviews, some employees are reviewed more than 5 times, some less than 5 times, and (depending on the alignment of the sun, the moon and the stars) it is possible that some may not be selected for review.
function s05648755803(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Sheet3";
var sheet = ss.getSheetByName(sheetname);
// some variables
var randomcount = 30; // how many random names
var rowstart = 7; // ignore row 1 - the header row
var width = 5; // how many names in each row - 1/rater plus 5/ratee
var thelastrow = sheet.getLastRow();
//Logger.log("DEBUG:last row = "+thelastrow)
// get the employee names
var employeecount = thelastrow-rowstart+1;
//Logger.log("DEBUG: employee count = "+employeecount);//DEBUG
// get the data
var datarange = sheet.getRange(rowstart, 1, thelastrow - rowstart+1);
//Logger.log("DEBUG: range = "+datarange.getA1Notation());//DEBUG
var data = datarange.getValues();
//Logger.log("data length = "+data.length);
//Logger.log(data);
var counter = 0;
var newarray = [];
for (c = 0;c<46;c++){
counter = c;
for (i=0;i<data.length;i++){
if(i!=counter){
newarray.push(data[i]);
}
}
//Logger.log(newarray);
var rowdata = [];
var results = selectRandomElements(newarray, 5);
Logger.log(results)
rowdata.push(results);
var newrange = sheet.getRange(rowstart+c, 3, 1, 5);
newrange.setValues(rowdata);
// clear the arrays for the next loop
var newarray=[];
var rowdata = []
}
}
/*
// selectRandomElements and getRandomInt
// Credit: Vidar S. Ramdal
// https://webapps.stackexchange.com/a/102666/196152
*/
function selectRandomElements(fromValueRows, count) {
var pickedRows = []; // This will hold the selected rows
for (var i = 0; i < count && fromValueRows.length > 0; i++) {
var pickedIndex = getRandomInt(0, fromValueRows.length);
// Pick the element at position pickedIndex, and remove it from fromValueRows. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
var pickedRow = fromValueRows.splice(pickedIndex, 1)[0];
// Add the selected row to our result array
pickedRows.push(pickedRow);
}
return pickedRows;
}
function getRandomInt(min,
max) { // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
Screenshot#1
Screenshot#2
Try this. Satisfies all the three requirements.
HTML/JS:
<html>
<title>Unique Employees</title>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
</head>
<table id="survey_table" border="1" width="85%" cellspacing="0">
<thead>
<th>Rater</th>
<th>Ratee1</th>
<th>Ratee2</th>
<th>Ratee3</th>
<th>Ratee4</th>
<th>Ratee5</th>
</thead>
<tbody id="table_body">
</tbody>
</table>
<script type="text/javascript">
function arrayRemove(arr, value) {
return arr.filter(function(ele) {
return ele != value;
});
}
function getRandomInt(rm_row, rm_col) {
var temp_arr = [];
for (var k = 1; k <= 46; k++) {
temp_arr.push(k);
}
for (var k = 0; k < rm_row.length; k++) {
temp_arr = arrayRemove(temp_arr, rm_row[k]);
}
for (var k = 0; k < rm_col.length; k++) {
temp_arr = arrayRemove(temp_arr, rm_col[k]);
}
var rand = temp_arr[Math.floor(Math.random() * temp_arr.length)];
return rand;
}
function exclude_num(row_unq, col_unq) {
var rand_int = getRandomInt(row_unq, col_unq);
if (!row_unq.includes(rand_int) && !col_unq.includes(rand_int)) {
arr_row.push(rand_int);
return rand_int;
} else {
return exclude_num(arr_row, arr_cols);
}
}
for (var i = 1; i <= 46; i++) {
var arr_row = [];
arr_row.push(i);
var table_html = '<tr id="Row' + i + '">';
for (var j = 1; j <= 6; j++)
{
if (j == 1) {
table_html += '<td class="Column' + j + ' cells_unq">' + i + '</td>';
} else {
var arr_cols = []
$('.Column' + j).each(function() {
arr_cols.push(Number($(this).text()));
});
var num = exclude_num(arr_row, arr_cols);
table_html += '<td class="Column' + j + ' cells_unq">' + num + '</td>';
}
}
table_html += '</tr>';
var row_html = $('#table_body').html();
$('#table_body').html(row_html + table_html);
}
$('.cells_unq').each(function() {
temp_text = $(this).text();
$(this).text('Name' + temp_text);
});
</script>
<style type="text/css">
td {
text-align: center;
}
</style>
</html>

Google sheets unique multiple columns and output multiple columns

I need some help working through this problem. I have multiple columns of data and I want to make it so that I only keep unique values and return the items to their respective columns.
1 2 3 6
1 1 4 7
2 3 5 8
would end up like this:
1 3 4 6
2 5 7
8
Right now I can do with one column using the =unique() function but I want to be able to put a new column of data and it would only spit out the unique items from that into the new table.
This is an attempt at doing it with an array formula: assumes cells do not contain negative numbers, commas or pipe symbols.
=ArrayFormula(transpose(split(transpose(split(join(",",text(unique(transpose(split(textjoin(",",true,{transpose(A1:D3),-transpose(column(A1:D3))}),","))),"0;|")),"|")),",")))
Also works with full-column references
=ArrayFormula(transpose(split(transpose(split(join(",",text(unique(transpose(split(textjoin(",",true,{transpose(A:D),-transpose(column(A:D))}),","))),"0;|")),"|")),",")))
var sheet = SpreadsheetApp.getActive().getSheetByName("Sheet5");
var sheet2 = SpreadsheetApp.getActive().getSheetByName("Sheet6");
var info = sheet.getDataRange().getValues();
var lastRow = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var seen = {}; // make object acts as a hash table
var data = info; // make array same size as original array that has the entire sheet
for (var x = 0; x < info[x].length; x++){
for (var i = 0; i < info.length; i++) {
if (!(info[i][x] in seen)) { // if item is not in seen
data[i][x] = info[i][x]; // put item in location
seen[data[i][x]] = true;}
else {
data[i][x] = "";}}} // if its not then add blank item to array
The previous answer had a join limit of 50,000 characters, so it had its own limit. this option helps for bigger sets of data. I think it could be tweaked still and improved
Paste these scripts in the script editor.
function onOpen() {
SpreadsheetApp.getUi().createMenu('My Menu')
.addItem('Show uniques', 'onlyShowUniques')
.addToUi()
}
function onlyShowUniques() {
var r, d, u, t, row, i, j;
r = SpreadsheetApp.getActive().getActiveRange();
d = transpose(r.getValues());
u = [];
t = [];
for (var i = 0, rl = d.length; i < rl; i++) {
row = []
for (var j = 0, cl = d[0].length; j < cl; j++) {
if (d[i][j] && (!~u.indexOf(d[i][j]) || i == 0 && j == 0)) {
u.push(d[i][j])
row.push(d[i][j])
} else {
row.push(null)
}
row.sort(function (a, b) {
return (a === null) - (b === null) || +(a > b) || -(a < b);
})
}
t.push(row)
}
r.setValues(transpose(t))
}
function transpose(array) {
return Object.keys(array[0])
.map(function (col) {
return array.map(function (row) {
return row[col];
});
});
}
Reopen the spreadsheet and see if an extra menu-item ('My Menu') is created.
Select the range you want to clear of duplicates.
Go to the menu item and select 'Show uniques'.
See if that brings about the expected output.

Need help looping though 3 loops to append data in Google Scripts

I am working with Col A, B & C. Col A contains A-E, Col B Contains 1, a, 3, b, 5 and Col C will be where I will store duplicated information (a and b would go into C1 & C2). Any help would be appreciated. In summary; compare A and B for similarity, output result into C
function appendString() {
var range = SpreadsheetApp.getActiveSheet().getRange("A1:A5");
var range2 = SpreadsheetApp.getActiveSheet().getRange("B1:B5");
var range3 = SpreadsheetApp.getActiveSheet().getRange("C1:C5")
var numRows = range.getNumRows();
var x = 0
// var numCols = range.getNumColumns();
j = 1 // row A
k = 2 // row B
m = 3 // row C
n = 1
// First loop though B
for (var i = 1; i <= numRows; i++) {
// set the current value...
var currentValue = range.getCell(i, j).getValue();
// In the log tell us the current value
Logger.log("Set A:" + currentValue);
// Loops though col B to compare to col A
for (var l = 1; l <= numRows; l++) {
// Sets the current value to compare value
var compareValue = range2.getCell(l, j).getValue();
Logger.log("Set B:" + compareValue)
// If the compareValue and the currentValue (match)
if (compareValue === currentValue) {
Logger.log("MATCHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH");
// We will write the result to col C down one row
for (n; n <= x; n++) {
// this makes it only run once'
range3.setValue(currentValue);
Logger.log("Appending.................");
x = n + 3
}
}
}
}
}
I think your problem statement boils down to this: Fill column C with a list of unique values that appear in both column A and B.
There is a built-in javascript Array method Array.indexOf() that makes it very easy to search for matching elements. As the problem is defined, we want to search in a column, so to use that method we need a column to be represented as an Array. The Range.getValues() method allows us to load a whole range of values at once, and delivers them as a two-dimensional array, with rows as the first dimension. We need columns there, and we can achieve that by a matrix transposition.
So here's what we end up with. There isn't a built-in transpose(), so I've included one. As we search for matches, results are stored in an Array C, using the built-in Array.push() method. Finally, array C is treated as a two-dimensional array, transposed, and written out to the sheet in column C.
function recordMatches() {
var range = SpreadsheetApp.getActiveSheet().getRange("A1:B5");
var data = range.getValues();
// For convenience, we'll transpose the data, so
// we can treat columns as javascript arrays.
var transposed = transpose(data);
var A = transposed[0],
B = transposed[1],
C = [];
// Go through A, looking for matches in B - if found, add match to C
for (var i=0; i < A.length; i++) {
if (B.indexOf(A[i]) !== -1) C.push(A[i]);
}
// If any matches were found, write the resulting array to column C
if (C.length > 0) {
var rangeC = SpreadsheetApp.getActiveSheet().getRange(1,3,C.length);
rangeC.setValues(transpose([C]));
}
}
function transpose(a) {
return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}

Categories