CouchDB list view error when no key requested - list

Having trouble with a list function I wrote using CouchApp to take items from a view that are name, followed by a hash list of id and a value to create a CSV file for the user.
function(head, req) {
// set headers
start({ "headers": { "Content-Type": "text/csv" }});
// set arrays
var snps = {};
var test = {};
var inds = [];
// get data to associative array
while(row = getRow()) {
for (var i in row.value) {
// add individual to list
if (!test[i]) {
test[i] = 1;
inds.push(i);
}
// add to snps hash
if (snps[row.key]) {
if (snps[row.key][i]) {
// multiple call
} else {
snps[row.key][i] = row.value[i];
}
} else {
snps[row.key] = {};
snps[row.key][i] = row.value[i];
}
//send(row.key+" => "+i+" => "+snps[row.key][i]+'\n');
}
}
// if there are individuals to write
if (inds.length > 0) {
// sort keys in array
inds.sort();
// print header if first
var header = "variant,"+inds.join(",")+"\n";
send(header);
// for each SNP requested
for (var j in snps) {
// build row
var row = j;
for (var k in inds) {
// if snp[rs_num][individual] is set, add to row string
// else add ?
if (snps[j][inds[k]]) {
row = row+","+snps[j][inds[k]];
} else {
row = row+",?";
}
}
// send row
send(row+'\n');
}
} else {
send('No results found.');
}
}
If I request _list/mylist/myview (where mylist is the list function above and the view returns as described above) with ?key="something" or ?keys=["something", "another] then it works, but remove the query string and I get the error below:
{"code":500,"error":"render_error","reason":"function raised error: (new SyntaxError(\"JSON.parse\", \"/usr/local/share/couchdb/server/main.js\", 865)) \nstacktrace: getRow()#/usr/local/share/couchdb/server/main.js:865\n([object Object],[object Object])#:14\nrunList(function (head, req) {var snps = {};var test = {};var inds = [];while ((row = getRow())) {for (var i in row.value) {if (!test[i]) {test[i] = 1;inds.push(i);}if (snps[row.key]) {if (snps[row.key][i]) {} else {snps[row.key][i] = row.value[i];}} else {snps[row.key] = {};snps[row.key][i] = row.value[i];}}}if (inds.length > 0) {inds.sort();var header = \"variant,\" + inds.join(\",\") + \"\\n\";send(header);for (var j in snps) {var row = j;for (var k in inds) {if (snps[j][inds[k]]) {row = row + \",\" + snps[j][inds[k]];} else {row = row + \",?\";}}send(row + \"\\n\");}} else {send(\"No results found.\");}},[object Object],[object Array])#/usr/local/share/couchdb/server/main.js:979\n(function (head, req) {var snps = {};var test = {};var inds = [];while ((row = getRow())) {for (var i in row.value) {if (!test[i]) {test[i] = 1;inds.push(i);}if (snps[row.key]) {if (snps[row.key][i]) {} else {snps[row.key][i] = row.value[i];}} else {snps[row.key] = {};snps[row.key][i] = row.value[i];}}}if (inds.length > 0) {inds.sort();var header = \"variant,\" + inds.join(\",\") + \"\\n\";send(header);for (var j in snps) {var row = j;for (var k in inds) {if (snps[j][inds[k]]) {row = row + \",\" + snps[j][inds[k]];} else {row = row + \",?\";}}send(row + \"\\n\");}} else {send(\"No results found.\");}},[object Object],[object Array])#/usr/local/share/couchdb/server/main.js:1024\n(\"_design/kbio\",[object Array],[object Array])#/usr/local/share/couchdb/server/main.js:1492\n()#/usr/local/share/couchdb/server/main.js:1535\n#/usr/local/share/couchdb/server/main.js:1546\n"}

Can't say for sure since you gave little detail, however, a probable source of problems, is the use of arrays to collect data from every row: it consumes an unpredictable amount of memory. This may explain why it works when you query for a few records, and fails when you query for all records.
You should try to arrange data in a way that eliminates the need to collect all values before sending output to the client. And keep in mind that while map and reduce results are saved on disk, list functions are executed on every single query. If you don't keep list function fast and lean, you'll have problems.

Related

Googlescript 'if' question - do only one of two tasks

I have 'else if' statements and I need it to be called only if the previous 'if' does not execute. First 'if' (check status) work perfect, second work to, but the 'else if' is done every time because there are different types in the table (A, B, C e.t.c)
Edit:
1. New line must be add if there's no open A;
2. If there are open more than one A - all are must be closed;
3. Ignore all other types and add new line only if there are no As open;
Here is my code:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('SheetName');
var data = sheet.getRange(2,1,sheet.getLastRow(),2).getValues();
var nextRow = SpreadsheetApp.getActiveSheet().getDataRange().getLastRow()+1;
for (var i=0; i<data.length; i++){
var row = data[i];
var type = row[0];
var status = row[1];
//check it always
if (status != 'close'){
//check first and if it's true don't do 'else if'
if (type == 'A') {
sheet.getRange(i+2,2).setValue('close')
}
//this should only be called if the previous 'if' is not true
else if (type != 'A' && type != ''){
var values = [['A','open']];
sheet.getRange("A"+nextRow+":B"+nextRow).setValues(values);
}
}
}
}
Flow:
Loop through all data to find if there's a "A:Open"
If found in all of data, just close that row
else, Add a new line: "A:Open"
Use a Boolean variable(isThereASingleAOpen) to keep track of A:Open found status
Sample script:
function closeAandAddOpenA() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('SheetName');
//Separate range and data
var range = sheet.getRange(2, 1, sheet.getLastRow(), 2);
var data = range.getValues();
//Is there a A Open in the data? Let's assume there's none
var isThereASingleAOpen = false;
//Loop through data array
for (var i = 0; i < data.length; i++) {
var row = data[i];
var type = row[0];
var status = row[1];
//If status is != close and type == 'A'
if (status != 'close' && type == 'A') {
//Change current row status to close in the data array
row[1] = 'close';
//Oops We were wrong. There was a A open in the data
isThereASingleAOpen = true;
}
} //Loop closed
//Set the modified data back to the range
//Batch operations are faster
range.setValues(data);
//if there is NOT a single A open in all of data, Append a new line
if (!isThereASingleAOpen) {
sheet.appendRow(['A', 'Open']);
}
}

How to alter this script to add another condition

I have this piece of code which has been working great for me, however, I need a minor alteration to it and don't know how to proceed.
I would like for 'Multiple Use' to be added as another condition, alongside 'Yes' for the onEdit() to work.
function numberToLetter(number){
// converts the column number to a letter
var temp = "";
var letter = "";
while (number > 0){
temp = (number - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
number = (number - temp - 1) / 26;
}
return letter;
}
function obtainFirstBlankRow() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Aug2019');
// search for first blank row
var col = sheet.getRange('A:A');
var vals = col.getValues();
var count = 0;
while (vals[count][0] != "") {
count++;
}
return count + 1;
}
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSheet();
if (ss.getName() == 'ProspectiveSites' && e.range.getColumn() == 26) {
if (e.range.getValue() != 'Yes'){
Logger.log('test');
return;
}
var sourceSheet = SpreadsheetApp.getActive().getSheetByName('ProspectiveSites');
var targetSheet = SpreadsheetApp.getActive().getSheetByName('Aug2019');
//Logger.log('O' + e.getRow() + ':O' + e.getRow());
Logger.log(e);
Logger.log(e.range.getValue());
var cell15 = sourceSheet.getRange('O' + e.range.getRow() + ':O' + e.range.getRow()).getValue();
var cell24 = sourceSheet.getRange('X' + e.range.getRow() + ':X' + e.range.getRow()).getValue();
Logger.log(cell15);
Logger.log(cell24);
var row = obtainFirstBlankRow();
targetSheet.getRange(row, 1).setValue(cell15);
targetSheet.getRange(row, 2).setValue(cell24);
}
}
Solution
What stops you from adding another condition for the if statement? Please, take some time to research JS documentation, it will greatly help you in the long run (see useful links after the sample).
Modifications
This modification assumes that you need to exit if value is not equal to "Multiple use" and not equal to "Yes". Also, note that there are a few additional changes made for optimization purposes (I changed all comparison operators to strict as well).
Sample
/**
* onEdit simple trigger;
* #param {Object} event object;
*/
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actS = ss.getActiveSheet(); //active sheet === source sheet;
//access event object params;
var range = e.range;
var row = range.getRow();
var column = range.getColumn();
var value = range.getValue();
if (actS.getName()==='ProspectiveSites' && column===26) {
if (value!=='Yes'&&value!=='Multiple Use') {
Logger.log('test');
return;
}
var augS = ss.getSheetByName('Aug2019'); //target sheet;
var cell15val = actS.getRange('O'+row+':O'+row).getValue();
var cell24val = actS.getRange('X'+row+':X'+row).getValue();
var rowBlank = obtainFirstBlankRow();
var target = augS.getRange(rowBlank,1,1,2); //idx, first col, 1 row, 2 cols;
target.setValues([[ cell15val , cell24val ]]);
}
}
Useful links
if..else statement reference on MDN;
Comparison operators reference on MDN;
getRange() method reference;
setValues() method reference;

Replace non-blank cells with 0

I tried to find solutions online however couldn't find one specifically for my need: I want to create a script which replaces non-blank cells in given column with 0.
Is there a simple solution for this?
Thanks.
Try:
function blankTo0() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s = ss.getActiveSheet()
var rng = s.getRange("A:A");//change to column you want
var data= rng.getValues()
for (var i=0; i < data.length; i++) {
if (data[i][0] == "") {
data[i][0] = 0;
} else if (data[i][0] == "") {
data[i][0] = data[i][0];
}}
rng.setValues(data); // replace old data with new
}

Adding items to a Google Forms list from a spreadsheet range

I am struggling to get past the last line in this code any help will be appreciated:
The error I am getting is "Array is empty: values (line 16, file "Code")". I have double checked the item ID, the spreadsheet ID and that there is data for it to pick up within the correct range. Any pointers or insights...?
function GetFleet() {
var ssDEFECTS = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var rngFLEET = ssDEFECTS.getDataRange();
var values = rngFLEET.getValues();
var FleetList = [];
//Use column 0 and ignore row 1 (headers)
for (var i = 1; i <= values.length; i++) {
var v = values[i] && values[i][0];
v && values.push(v)
}
// Form ID & List ID
var DefectsForm = FormApp.openById('<FORM KEY ID>');
DefectsForm.getItemById(794194842).asListItem().setChoiceValues(FleetList);
};
Nothing is pushed to your array FleetList. Also the coding in your loop is faulty.
Assuming you want to push the first column (not including the header),
try this and see if it works
function GetFleet() {
var values = SpreadsheetApp.getActiveSpreadsheet()
.getSheets()[0].getDataRange()
.getValues();
var FleetList = [];
//Use column 0 and ignore row 1 (headers)
for (var i = 1, len = values.length; i < len; i++) {
FleetList.push(values[i][0])
}
// Form ID & List ID
var DefectsForm = FormApp.openById('<FORM KEY ID>');
DefectsForm.getItemById(794194842)
.asListItem()
.setChoiceValues(FleetList);
};

copy a list to a SpreadSheetGear Irange

I have the following code:
using (CPASEntities ctx = new CPASEntities())
{
IWorksheet ws = wb.Worksheets[0];
ws.Name = "Summary";
var tsm = (from x in ctx.tblTimesheetMasters
where x.TSID == TSID
select new
{
TimesheetID = x.TSID,
Comments = x.TSComments,
Vendor = x.tblVendor.Vendor_Name,
StartDate = x.TSStartDate,
Author = x.TSAuthor,
Approver = x.TSApprover,
Override_Approver = x.TSOverrideApprover,
Status = x.tblTimesheetStatu.TSStatusDesc
}
).ToList();
SpreadsheetGear.IRange range = ws.Cells["A1"];
// I want to copy the entire tsm list to this range, including headings.
}
As the comment states, I want to put that entire list into the ws worksheet starting at A1. I include the code in case it's easier to use a different construct. FWIW, there will be only one entry...TSID is the primary key. I can, of course, use the .FirstorDefault() construct if that is important. I thought it not important.
Your range is only one cell. You need a range big enough to contain all the cells the list would populate.
To populate your worksheet with the list, you could do something like this.
int iRow = 0;
int iCol = 0;
if (tsm.Count() > 0)
{
foreach (var prop in tsm[0].GetType().GetProperties())
{
ws.Cells[iRow, iCol].Value = prop.Name;
iCol++;
}
iRow++;
foreach (var t in tsm)
{
iCol = 0;
foreach (var prop in t.GetType().GetProperties())
{
ws.Cells[iRow, iCol].Value = prop.GetValue(t, null);
iCol++;
}
iRow++;
}
}
If you want a range, you could add this line.
SpreadsheetGear.IRange range = ws.Cells[0, 0, iRow - 1, iCol - 1];