Javascript to get the Random sample of data - javascript

Hi all,
I am working on a dataset on Google Sheet and I need to get the random data (specified number) by the col 3 names to audit I am using google app script for the same but couldn't able to get the data. Here's the code I tried but I don't want the % of data I want the equally distributed random data for each employee in col 3.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var trn = ss.getSheetByName('Data');
var originalData = trn.getRange(2, 1, trn.getLastRow() - 1, 3).getValues();
var ReviewerEmail = data;
var data = originalData.filter(function(item) {
return item[1] === 'rob' || item[1] === 'john' || item[1] === 'toger';
});
//Logger.log(data);
var targetsheet = ss.insertSheet(Reviewer);
targetsheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
function getsampledata() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('A:C');
var values = range.getValues();
var headers = values.shift();
var nameColumn = 1;
var uniqueNames = values
.map((row) => row[nameColumn])
.filter((item, i, ar) => ar.indexOf(item) === i)
.filter(String);
var data = [headers];
uniqueNames.forEach(function(name) {
var nameEntries = values.filter((row) => row[nameColumn] == name);
var entries = nameEntries.length;
var tenth = Math.round((entries / 100) * 27.35); //Sampling Percentage
for (i = 0; i < tenth; i++) {
var random = Math.floor(Math.random() * nameEntries.length);
data.push(nameEntries[random]);
nameEntries.splice(random, 1);
}
});
return data;
}

If you want a specific number of sample, then use a parameter to indicate how many you want for each.
Whole working script:
function getsampledata(sample) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('A:C');
var values = range.getValues();
var headers = values.shift();
var nameColumn = 2;
var uniqueNames = values
.map((row) => row[nameColumn])
.filter((item, i, ar) => ar.indexOf(item) === i)
.filter(String);
var data = [headers];
uniqueNames.forEach(function(name) {
var nameEntries = values.filter((row) => row[nameColumn] == name);
for (i = 0; i < sample; i++) {
var random = Math.floor(Math.random() * nameEntries.length);
data.push(nameEntries[random]);
nameEntries.splice(random, 1);
}
});
return data;
}

Related

JavaScript: else if won't work in for loops [Spreadsheet]

I am trying to loop through the whole row in my google sheet and break it at the last row +1
'i' as an indicator for stopping the loop, using else-if condition.
But somehow the loop won't go through else-if condition.
The loop stops when 'i' doesn't meet the for-loop condition, How can I make it stop when meeting else-if condition instead ?
I'm fresh and new to Javascript and Appscript, will grateful for every advise.
function makedict(){
var product = {}
var code = code_dicted
var name = name_dicted
var unit = unit_dicted
var balance = balance_dicted
var value0 = sheet.getRange("C:M").getValues().map(row => [ row[0]]); //code
var value1 = sheet.getRange("C:M").getValues().map(row => [ row[1]]); //name
var value5 = sheet.getRange("C:M").getValues().map(row => [ row[5]]); //bal
var value6 = sheet.getRange("C:M").getValues().map(row => [ row[10]]); //unit
var range = sheet.getRange("C:M").getValues().length
var lastRow = sheet.getLastRow()
var dict = {}
var result = "\n\n\n"
for (i = 1;i < range;i++){
dict["code"] = value0[i]
var code_dicted = dict["code"]
var code = code_dicted
dict["name"] = value1[i]
var name_dicted = dict["name"]
var name = name_dicted
dict["balance"] = value5[i]
var balance_dicted = dict["balance"]
var balance = balance_dicted
dict["unit"] = value6[i]
var unit_dicted = dict["unit"]
var unit = unit_dicted
product["detail"] = {code,name,balance,unit}
// console.log(product)
if (product.detail["balance"]<product.detail["unit"]){
var text = product.detail["code"]+product.detail["name"]+product.detail["balance"]+product.detail["unit"];
result = result.concat(text)+"\n\n"
}
else if (i === lastRow+1){
console.log(result)
break
}
}
Solved
Thank you. I've solved it following the above advise.
using While-loop instead.
var i = 1;
while (i < range){
dict["code"] = value0[i]
var code_dicted = dict["code"]
var code = code_dicted
dict["name"] = value1[i]
var name_dicted = dict["name"]
var name = name_dicted
dict["balance"] = value5[i]
var balance_dicted = dict["balance"]
var balance = balance_dicted
dict["unit"] = value6[i]
var unit_dicted = dict["unit"]
var unit = unit_dicted
i = i+1
product["detail"] = {code,name,balance,unit}
// console.log(product)
if (product.detail["balance"]<product.detail["unit"]){
var text = product.detail["code"]+product.detail["name"]+product.detail["balance"]+product.detail["unit"];
result = result.concat(text)+"\n\n"
}
else if (i === lastRow+1){
console.log(result)
break
}
}
for (i = 1;i < range + 1 ;i++){
Try the above code, I'm assuming lastRow is a number and it ranges from 0 to range

Google Sheets Random Copy and Sort with Scripts

What I'm attempting to do is copy a column over and re-sort it. Problem is, it captures all available cells and uses the same space to re-sort, causing blank spaces. The idea is to create tournament match pairings, with the first column being the Roster itself, and following columns being players they will be matched against.
I'd also like to add a line that verifies a name doesn't appear twice on the same row, reshuffling until the column is all unique along each row
This is the code I have so far. I attempted to filter the data by swapping
range2.setValues(shuffleArray(range.getValues()));
for
range2.setValues(shuffleArray(range.getValues().filter(String)));
but this results in a "Number of data rows is 10 when range is 41" error, not verbatim obviously. I'm trying to collapse the blank spaces that are shown in this Screenshot.
I'm sure I can figure out how to expand it by however many matches I wish to generate.
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var range = sheet.getRange('A31:A')
var range2 = sheet.getRange('C31:C');
range2.clearContents;
range2.setValues(shuffleArray(range.getValues()));
}
function shuffleArray(array) {
var i, j, temp;
for (i = array.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i+1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
EDIT::::: Code has been moved to a test sheet hence different name and ranges, ive adjusted the samples when i moved them of course
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet4');
var range = sheet.getRange('A1:A40')
var v = range.getValues().filter(String);
//Match 1
var values = shuffleArray1(v);
while (v.length != [...new Set(values.map(([a]) => a))].length) {
values = shuffleArray1(v);
}
range.offset(0, 1, values.length).setValues(values);
//Match 2
var values2 = shuffleArray2(v);
while (v.length != [...new Set(values2.map(([a]) => a))].length) {
values = shuffleArray2(v);
}
range.offset(0, 2, values.length).setValues(values2);
}
function shuffleArray1(array) {
var i, j, temp;
for (i = array.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i+1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function shuffleArray2(array) {
var u, v, temp;
for (u = array.length - 3; u > 0; u--) {
v = Math.floor(Math.random() * (u+2));
temp = array[u];
array[u] = array[v];
array[v] = temp;
}
return array;
}
Modification points:
I think that range2.clearContents might be range2.clearContent().
In your script, by sheet.getRange('A31:A'), all rows in the sheet are retrieved.
When these points are reflected in your script, how about modifying shuffleRange() as follows?
Modified script:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var lastRow = sheet.getLastRow();
var range = sheet.getRange('A31:A' + lastRow);
var range2 = sheet.getRange('C31:C' + lastRow);
range2.clearContent();
var values = shuffleArray(range.getValues()).filter(String);
range.offset(0, 2, values.length).setValues(values);
}
I'm not sure about the last row of your sheet. So, I proposed the above modification.
Added 1:
From your following new question,
essentially if the row contains a duplicate it has to reshuffle until each row contains a unique name from the original column, to create unique match pairings for tournaments, this will check the whole row, as some tournaments run only 2 matches, some up to 21
In this case, how about the following sample script?
Sample script:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var lastRow = sheet.getLastRow();
var range = sheet.getRange('A31:A' + lastRow);
var range2 = sheet.getRange('C31:C' + lastRow);
range2.clearContent();
var v = range.getValues().filter(String);
var values = shuffleArray(v);
while (v.length != [...new Set(values.map(([a]) => a))].length) {
values = shuffleArray(v);
}
range.offset(0, 2, values.length).setValues(values);
}
In this case, when the duplicated values are included in values, shuffleArray function is run again.
Added 2:
From your following reply,
Unfortunately it produced duplicate lines almost immediately once i duplicate the functions to create a second set of results
I added a new sample so you can see how im trying to expand it across several columns of results, this will create a set number of matches. I will, when done, swap the counter for a cell check so a user can set the match number, but thats later
Sample script:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var range = sheet.getRange('A1:A40');
var v = range.getValues().filter(String);
var createValues = v => {
SpreadsheetApp.flush(); // I'm not sure whether this line is required.
var temp = sheet.getRange(1, 1, 40, sheet.getLastColumn()).getValues();
var existValues = temp[0].map((_, c) => temp.map(r => r[c]).join("")).filter(String);
var values;
do {
values = shuffleArray1(v);
while (v.length != [...new Set(values.map(([a]) => a))].length) {
values = shuffleArray1(v);
}
var check = values.map(([a]) => a).join("");
} while (existValues.some(e => e == check));
return values;
}
var values1 = createValues(v);
range.offset(0, 1, values1.length).setValues(values1);
var values2 = createValues(v);
range.offset(0, 2, values2.length).setValues(values2);
}
In this modification, the new column values are created by checking all existing columns.
Adding to Tanaike's suggestion I've joined your two functions in order to be able to re-shuffle. I'm not as well-versed in coding, and probably there's a more-alike version of your code that also enables the re-shuffling. But you can try this:
function shuffleRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('SETUP');
var lastRow = sheet.getLastRow()
var range = sheet.getRange('A31:A' + lastRow);
var range2 = sheet.getRange('C31:C' + lastRow);
range2.clearContents;
function shuffleArray() {
var i, j, temp;
var array = range.getValues()
var array2 = range.getValues()
var count= 1;
while (count>0){
count=0
for(i=array.length-1;i>0;i--){
j = Math.floor(Math.random() * (i+1));
temp = array2[i];
array2[i] = array2[j];
array2[j] = temp;
}
for(i=0;i<array.length;i++){
if(array[i].toString() == (array2[i]).toString()){count = count+1}
}}
return array2
}
range2.setValues(shuffleArray())
}
I've make it try tenths of times and never got a duplicate:

How to improve this nested for loop using GAS?

I'd like to learn an alternative way to run this for loop, because currently, it sets the value through each iteration and as this can get large, it better be more performant.
function calculate() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const settingSheet = ss.getSheetByName('settings');
const dataSheet= ss.getSheetByName('Data');
let settingData = settingSheet.getRange(1, 1, settingSheet.getLastRow(), settingSheet.getLastColumn()).getValues();
let spcData = dataSheet.getRange(1, 1, dataSheet.getLastRow(), dataSheet.getLastColumn()).getValues();
let recordTypesAccepted = ['strategy1', 'goal2'];
var settingPar1 = settingData.filter(e => e[0] == 'default').map(e => e[1]);
var settingPar2 = settingData.filter(e => e[0] == 'default').map(e => e[2]);
for (let a = 0; a < spcData.length; a++) {
for (let r = 0; r < settingData .length; r++) {
let settingSearchTerm = settingData[r][4];
let spcSearchTerm = spcData[a][10];
let settingMatchType = settingData[r][5];
let spcMatchType = spcData[a][9];
let spcRecordType = spcData[a][8];
if (recordTypesAccepted.indexOf(spcRecordType) > -1 && settingMatchType === spcMatchType) {
if (settingSearchTerm == spcSearchTerm) {
settingPar1 = settingData[r][7];
settingPar2 = settingData[r][6];
}
let spcClicks = spcData[a][10];
let spcOrders = spcData[a][11];
let row = a + 1;
if (spcClicks >= settingsPar1 && spcOrders >= settingsPar2) {
let newValue = 10;
spcSheet.getRange(row, 20).setValue(newBid).setBackground("#D8E271");
}
}
}
}
}
Thank you!!!
Description
Provided using setValues() doesn't overwrite any formulas you could use getValues() to get an array of the old values, replace any that need to be updated and simply put back the array of values using setValues(). The same for back grounds and number formats.
Note it is assumed from the OP that spcSheet is defined elsewhere.
Script
function calculate() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const settingSheet = ss.getSheetByName('settings');
const dataSheet= ss.getSheetByName('Data');
let settingData = settingSheet.getRange(1, 1, settingSheet.getLastRow(), settingSheet.getLastColumn()).getValues();
let spcData = dataSheet.getRange(1, 1, dataSheet.getLastRow(), dataSheet.getLastColumn()).getValues();
let recordTypesAccepted = ['strategy1', 'goal2'];
var settingPar1 = settingData.filter(e => e[0] == 'default').map(e => e[1]);
var settingPar2 = settingData.filter(e => e[0] == 'default').map(e => e[2]);
let spcRange = spcSheet.getRange(1,20,spcSheet.getLastRow(),1)
var spcValues = spcRange.getValues();
var backgrounds = spcRange.getBackgrounds();
var numberformats = spcRange.getNumberFormats();
for (let a = 0; a < spcData.length; a++) {
for (let r = 0; r < settingData .length; r++) {
let settingSearchTerm = settingData[r][4];
let spcSearchTerm = spcData[a][10];
let settingMatchType = settingData[r][5];
let spcMatchType = spcData[a][9];
let spcRecordType = spcData[a][8];
if (recordTypesAccepted.indexOf(spcRecordType) > -1 && settingMatchType === spcMatchType) {
if (settingSearchTerm == spcSearchTerm) {
settingPar1 = settingData[r][7];
settingPar2 = settingData[r][6];
}
let spcClicks = spcData[a][10];
let spcOrders = spcData[a][11];
if (spcClicks >= settingsPar1 && spcOrders >= settingsPar2) {
let newValue = 10;
spcValues[a][0] = newBid;
backgrounds[a][0] = "#D8E271";
numberformats[a][0] = "€##0.00";
}
}
}
}
spcRange.setValues(spcValues);
spcRange.setBackgrounds(backgrounds);
spcRange.setNumberFormats(numberformats);
}
Reference
Range.getBackgrounds()
Range.setBackgrounds()
Range.getNumberFormats()
Range.setNumberFormats()

How to loop through all columns in spreadsheet?

I have a function that change the background color of cells.
Function works well and do the thing I want, but I met one problem that I don't really know how to solve.
I want this function to loop through all used columns in spreadsheet. (for now it is from G till TP column will increase)
As you can see the function I have now do the thing only with G column.
How to make it loop till the last used column?
function insertColor2() {
const sheetName = "結果1"; // set the sheet name.
// 1. Retrieve values from sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const values1 = sheet.getRange(3, 7, sheet.getLastRow() - 2,1).getValues();
const values = [];
values.push(values1);
// 2. Create an array for modifying the background colors.
const backgroundColors = values.map(([,,c,,,...g]) =>
g.reduce((o, e) => {
if (e.toString() != "") {
o.total += e;
o.colors.push(c >= o.total ? null : "red");
} else {
o.colors.push(null);
}
return o;
}, {colors: [], total: 0}).colors
);
const flatten = [].concat.apply([], backgroundColors);
const newArr = [];
while(flatten.length) newArr.push(flatten.splice(0,1));
Logger.log(newArr);
// 3. Modify the background colors of cells.
sheet.getRange(8, 7, newArr.length, 1).setBackgrounds(newArr);
}
So I found a solution to solve this problem. It is not the best solution but works well. I just simply transposed array twice at the beginning and before inputting result to the sheet.
function transpose(a) {
return Object.keys(a[0]).map(function(c) {
return a.map(function(r) { return r[c]; });
});
}
function transpose1(original) {
var copy = [];
for (var i = 0; i < original.length; ++i) {
for (var j = 0; j < original[i].length; ++j) {
// skip undefined values to preserve sparse array
if (original[i][j] === undefined) continue;
// create row if it doesn't exist yet
if (copy[j] === undefined) copy[j] = [];
// swap the x and y coords for the copy
copy[j][i] = original[i][j];
}
}
return copy;
}
function insertColor5() {
const sheetName = "結果1"; // Please set the sheet name.
// 1. Retrieve values from sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const values1 = sheet.getRange(3, 7, sheet.getLastRow() - 2, sheet.getLastColumn()).getValues();
const values = transpose(values1);
//Logger.log(values);
// 2. Create an array for modifying the background colors.
const backgroundColors = values.map(([,,c,,,...g]) =>
g.reduce((o, e) => {
if (e.toString() != "") {
o.total += e;
o.colors.push(c >= o.total ? null : "red");
} else {
o.colors.push(null);
}
return o;
}, {colors: [], total: 0}).colors
);
const kolorki = transpose1(backgroundColors);
//Logger.log(transpose1(backgroundColors));
// 3. Modify the background colors of cells.
sheet.getRange(8, 7, kolorki.length, kolorki[0].length).setBackgrounds(kolorki);
}

Picking a few random properties from an Object

If I have a few objects as below:
var points = {'p1':{x:1,y:1,z:2}, 'p2':{x:2,y:1,z:4},.....,'p10':{x:3,y:2:z:2}};
var lines = {'l1':{....},'l2':{....},......,'l10'{....}};
var jsonObject = {'info': 'information', 'points': points, 'lines': lines};
How do I pick (for example, 3 random points and 3 random lines) from respective objects and update in jsonObject such that the new jsonObject now contains {'info': 'information', 'points': pointObj, 'lines': lineObj}; where pointObj contains 3 random points from the orighinal points object and lineObj contains 3 random lines from the original lines object?
As its an Object with keys, you can use Object.keys to get its all keys as an array:
var getRandoms = function(source, number) {
var keys = Object.keys(source) // get all keys as array
,len = keys.length; // keys count
var result = {};
// not enough points, so just return all object from source.
if (len < number) {
return source;
}
var rand, key, count = 0;
while (count < number) {
rand = Math.floor(Math.random() * len);
key = keys[rand];
result[key] = source[key];
// Remove used key
keys.splice(rand, 1);
--len;
++count;
}
return result;
}
jsonObject.points = getRandoms(points, 3);
jsonObject.lines = getRandoms(lines, 3);
Create a function which return randomly chosen number between [0-10)
function get3RandomPoint(){
var temp, arr=[];
while(arr.length < 3) {
temp = Math.ceil(Math.random()*10);
if(arr.indexOf(temp) === -1) {
arr.push(temp);
}
}
return arr;
}
var points = {'p1':{x:1,y:1,z:2}, 'p2':{x:2,y:1,z:4},.....,'p10':{x:3,y:2:z:2}};
var lines = {'l1':{....},'l2':{....},......,'l10'{....}};
var randomNums = get3RandomPoint();
var randomPoints = {p1: points["p"+ randomNums[0]], p2: points["p"+ randomNums[1]], p2: points["p"+ randomNums[2]]};
randomNums = get3RandomPoint();
var randomLiness = {l1: points["l"+ randomNums[0]], l2: points["l"+ randomNums[1]], l2: points["l"+ randomNums[2]]};
var jsonObject = {'info': 'information', 'points': randomPoints, 'lines': randomLiness};
console.log(jsonObject);
Use a random() function.
var randomNum = Math.floor((Math.random() * 10));
Now based on the randomNum pick an entry using it's index.
var jsonObject = {
"points" : [],
"lines" : []
};
var count = 0;
while(count <3)
{
var randomNum = Math.floor((Math.random() * 10))
jsonObject["points"].push(points[randomNum][0]);
randomNum = Math.floor((Math.random() * 10));
jsonObject["lines"].push(lines[randomNum][0]);
count++;
}

Categories

Resources