This one might be a bit basic and easy to answer, but I've been pulling out my hair for a while now!
I've built the following code - which is semi-pseudo as I can't find the right way to make things work!
var s = "Test";
function onEdit(event)
{
var ss = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if (ss.getName() == s)
{
results = {"Currently On": 0, "Next Up": 0, "On Hold": 0, "Waiting on someone else": 0, "zDone": 0};
last = ss.getMaxRows();
start = ss.getRange("F3:"+last).getValues();
var output = "J11";
for (x=0;x<start.length;x++)
{
results[start[x]]++;
}
for (y=0;y<results.length;y++)
{
row = ss.getRow(output);
row.value = results[y];
output++;
}
}
}
I've got an example of the data in this image
The basic idea is to run through all the possible categories of each task and keep a numeric list on the side of how many of each there are. I'd also like to make it dynamic (so I don't have to hard code in the list of categories) but I'm more interested in just making it work for the moment.
The Google Apps debugger is very frustrating!
Thanks for your help all!
Firstly, this particular use case would be easily achievable with a spreadsheet formula, eg:
=QUERY(A2:F;"select F, count(A) where F != '' group by F label count(A) 'Count'";1)
but there may be a reason why you want to do this with GAS.
So secondly, this is where I think there may be some syntax issues:
last = ss.getMaxRows();
I would just use var last = ss.getLastRow() here.
start = ss.getRange("F3:"+last).getValues();
The range reference would evaluate to something like "F3:100", which is a valid reference in GSheets (don't know about whether GAS can handle it), but nevertheless you really want something like "F3:F100", so I would use var start = ss.getRange("F3:F"+last).getValues();.
results[start[x]]++;
When you create an array from a getValues() call it is a 2D array, so you would need to use results[start[x][0]]++;.
With the next loop and the output variable, I must admit I'm a bit lost with what you're doing there. How did you want your result table laid out?
You have
output = "J11";
And then you do
ss.getRow(output);
output++;
This is invalid.First of all, ss is a Sheet under which there is not getRow method. So, what you should really be doing is something like this
var row = 11 ;
var col = 10 ; //Col J
for (y=0;y<results.length;y++)
{
ss.getRange(row,col,1,1).setValue(results[y]);
row++:
}
Like AdamL, I suggest that this is better handled within the native capability of the spreadsheet. Seems to me that you want a pivot table, which would update dynamically. Alternatively a formula like =countif(F:F,"Currently On" ) would meet your immediate request. =Unique(F:F) will give you the list of categories in an array
Related
I know this question might sound wierd. But i am a noob to google sheets and trigger operations.Here's the problem, Lets say I have two cols in a sheet col_Link and col_value. col_link contains a link to another sheet and second one is obvious. I have to get the value from the col_link first and perform some operations then while performing second operation I still need this col_Link's link.
Now i have a installed onEdit() trigger which performs this operation but Im stuck on this little thing.
Code:
var c = e.range.getColumn();
var r = e.range.getRow();
var val = e.range.getValue();
if (c==1){
Link_operation(val,row,col); // this val contains link.
}
else if(c==2){
Value_operation(link,val,row,col);
// here val is some value. I want this function to somehow also has the link from col_Link.
}
Since the value can only contain either the link or the value.
What should be my approach to get this? Should I change the way I'm approaching this problem or is it possible to achieve multiple values like this?
Will appreciate any help.
Your question is a little vague and probably should include a screenshot or better detail of what exactly is your expected outcome.
If you want to get multiple values from an onEdit(e) trigger that weren't in the edited range then as master suggested, offsetting is the way to do it.
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var c = e.range.getColumn();
var r = e.range.getRow();
var val = e.range.getValue();
var link = sheet.getRange(r, c - 1).getValue();
Logger.log(link);
}
In this example if the cell B6 is edited then the value in A6 will be logged.
I am new to js and hope this is not too trivial, but I am unable to find any help on the net.
I wish to output to console.log and prevent moving to a new line, so the next time the output will be appended to the same line. ie,
"use strict";
for (let i = 0; i<=9;i++){
console.log(i); // here i would like to freeze the output so the result is 0123456789 on one line, rather than those digits in a column.
}
I have seen fixes involving assigning the outputs to a string and printing in 1 hit, but that seems incredibly crude. Even in Fortran 4 as I recall in the '70s, you could prevent moving to a new line before printing again, so I think I am missing something fundamental. Also I cannot find any general help on formatting numerical output in javascript. Can someone point me in the right direction?
Thanks
Unfortunately, the console.log() method will only write out a string to a single line and doesn't support the appending behavior you are looking for.
As you detailed in your original post, you could accomplish writing the final result out through the use of a variable (i.e. displaying the final concatenated string), but not continually appending to the same line within the console itself as the loop is being iterated over.
Alternative Grouping Option
The concept of grouping entries is supported, which is obviously very different than your original ask, but it may be worth considering as mentioned in the documentation for console.group() and might look something like this:
var rollingConcatenation = '';
console.group("Looping Group Example");
for (let i = 0; i<=9;i++){
rollingConcatenation += i;
console.log(rollingConcatenation);
}
console.groupEnd();
This can give your console the following appearance, which can help with readability (depending on your use cases):
Do It Yourself Implementation
Another option might be to store your current console value within a variable and at clear it and rewrite the updated values out. Depending on your very specific use cases, you could achieve the behavior you are looking for using something like this crude implementation:
// Define a custom console
var customConsole = {
// Store a reference to your backing value
tempValue: '',
// Always write out the most recent value
log: function(msg) {
this.tempValue += msg;
console.clear();
console.log(this.tempValue);
},
// A clear method to clear the backing console
clear: function() {
this.tempValue = '';
console.clear();
}
}
for (var i = 0; i < 10; i++) {
// Use your custom console instead of the normal one
customConsole.log(i);
}
Take a new variable outside the loop and then prepare that string inside the loop and then you can console.log() outside the loop.
var str = '';
for (let i = 0; i <= 9; i++) {
str += i;
}
console.log(str);
I was wondering if anyone could help me solve this issue or point me towards the right direction.
In my project we have a filed that needs to be autofilled, at this moment I use onblur which works wonders as it only does it so once you leave the focus. However, due to recent changes, it needs to only do so when there is only one unique item in the map which it matches the input.
I have a large array defined as following:
var myArray = [
[content, content],
[content, content],
...
]
Later in my code I associate it with a map, at least this is what most stackoverflow questions I looked at referred to it as follows:
var myMap = {};
for(0 to myArray.length) {
var a = myArray[i][0];
var b = myArray[i][1];
myMap[a] = b;
}
Now, finally I iterate over this array as follows:
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0)
the above is the line of code I am struggling to figure out how to change. At this moment, while using on blur, if I type in the letter 'A' for example, and leave the focus area it will automatically fill it in with a certain name. However, in the array there are many other objects that begin with, or contain A. How can I change it so that the onkeydown event will keep going until it finally filters it down to to only possible key-value pair? I tried looking at MDN's documentation for filtering, but I do not think that will work for my purposes, or at least I am too inexperienced with JS.
If the indexOf the first and last are nonnegative and equal, there is just one. You could do this with an && and boolean short circuit evaluation, but that will run very far right off the screen, so I am showing your code with one more nested if (up to you to add the end of the block). But we also need to see if there are matches on multiple keys.
var matchCount=0;
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0){
if (map[key].toLowerCase().indexOf(location.toLowerCase()) == map[key].toLowerCase().lastIndexOf(location.toLowerCase())) {
matchCount++;
then outside your for loop:
if (matchCount==1){ //do your stuff
Im currently setting up a decision matrix with Google Spreadsheet and I want to implement a ranking feature for the results. I wouldnt do this, if there were only 4 options, but in my case there are like 32 options available...
I tried to do this with the google script api, but the result is kinda disappointing:
function Ranking() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var alldata = sheet.getDataRange().getValues();
var data = [];
var j = 8;
for (var i = 0; i <= 31; i++) {
data [i] = alldata[18][j];
j= j+2;
}
var sorted = data.slice().sort(function(a,b){return b-a});
var ranks = data.slice().map(function(v){return sorted.indexOf(v)+1});
sheet.getRange("I20").setValue(ranks[0]);
sheet.getRange("K20").setValue(ranks[1]);
sheet.getRange("M20").setValue(ranks[2]);
sheet.getRange("O20").setValue(ranks[3]);
sheet.getRange("Q20").setValue(ranks[4]);
sheet.getRange("S20").setValue(ranks[5]);
sheet.getRange("U20").setValue(ranks[6]);
sheet.getRange("W20").setValue(ranks[7]);
sheet.getRange("Y20").setValue(ranks[8]);
sheet.getRange("AA20").setValue(ranks[9]);
sheet.getRange("AC20").setValue(ranks[10]);
sheet.getRange("AE20").setValue(ranks[11]);
sheet.getRange("AG20").setValue(ranks[12]);
sheet.getRange("AI20").setValue(ranks[13]);
sheet.getRange("AK20").setValue(ranks[14]);
sheet.getRange("AM20").setValue(ranks[15]);
sheet.getRange("AO20").setValue(ranks[16]);
sheet.getRange("AQ20").setValue(ranks[17]);
sheet.getRange("AS20").setValue(ranks[18]);
sheet.getRange("AU20").setValue(ranks[19]);
sheet.getRange("AW20").setValue(ranks[20]);
sheet.getRange("AY20").setValue(ranks[21]);
sheet.getRange("BA20").setValue(ranks[22]);
sheet.getRange("BC20").setValue(ranks[23]);
sheet.getRange("BE20").setValue(ranks[24]);
sheet.getRange("BG20").setValue(ranks[25]);
sheet.getRange("BI20").setValue(ranks[26]);
sheet.getRange("BK20").setValue(ranks[27]);
sheet.getRange("BM20").setValue(ranks[28]);
sheet.getRange("BO20").setValue(ranks[29]);
sheet.getRange("BQ20").setValue(ranks[30]);
sheet.getRange("BR20").setValue(ranks[31]);
}
As you can see, the code is really sloppy. Thats probably because I have never done javascript before and my coding knowledge is very basic in general.
Is there a way to implement such a feature directly in the spreadsheet, without the need of the script api?
Thanks in advance for the help!
for(i=0;i<numRanks;i++)
sheet.getRange(20, i*2+9).setValue(ranks[i]);
Basically you just iterate a for loop over the twentieth row every other column.
Function documentation here.
Jason's answer is clear & short - but it's not efficient because of the number of calls to the Spreadsheet Service.
For background about this concern, see Google Apps Script Best Practices. You may also want to look at What is faster: ScriptDb or SpreadsheetApp?.
You've already got an Array of ranks; if you transpose that into a two-dimensional array with the values in rows, then you can write it all out in one setValues() operation.
sheet.getRange("I20:BR20").setValues(transpose([ranks]));
Here's a transpose function, from Google Spreadsheet Script - How to Transpose / Rotate Multi-dimensional Array?.
function transpose(a)
{
return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}
There is still iteration happening, but it's in the transpose function now, instead of around the setValue() call, so this will run much quicker.
I need to take a textbox that is full of formatted info about accounts and then sort it somehow. I would like to know if it would be ideal (I'm trying to make this as efficient as possible) to parse the info into a two dimensional array, or if I should make account objects that will hold info in fields.
The program is simply meant to format the data so that it can be printed out without having to copy/paste.
So far I have...
function generateOutputfvoc()
{
var accountLines = document.getElementById('accountLines').value;
var accountLinesTemp = accountLines.split(/[\s]/);
for(var i = 0; i < accountLinesTemp.length; i++)
{
if(accountLinesTemp[i].match(/
Edit (1-18-13): Here is an example input. It is basically text copied from a web CRM tool. Note, this example input is something I typed up randomly.
P8B000001234567 stackoverflow Thing 12522225555 444 Active 2005-02-26 CO1000123456
P8B000001234568 stackoverflow Another Thing 444 Active 2005-02-26 CO1000123456
P8B000001234569 stackoverflow Another Thing 556 Active 2005-02-26 CO1000123456
I would like my program to take the text and simply output the text like this:
P8B000001234567 stackoverflow Thing 12522225555 444 Active 2005-02-26 CO1000123456
P8B000001234568 stackoverflow Another Thing 444 Active 2005-02-26 CO1000123456
P8B000001234569 stackoverflow Another Thing 556 Active 2005-02-26 CO1000123456
Also, I would like to know if I should use jQuery variables. I asked this because I have been looking online a lot and I found examples that use code that looks like this:
$check=fcompcheck();
if($check)
{
$output=document.frm1.type.value+" / ";
$output=$output+"Something - "+document.frm1.disco.value+" / ";
Note the: $output variable. The dollar sign indicates a jQuery variable, right?
Thank you for any help you might be able to offer me.
Update (1-19-13): I've taken a shot at it, but I'm making slow progress. I'm used to programming Java and my JavaScript looks too similar, I can tell I'm makings errors.
I'm taking it one step at a time. Here is the logic I'm using now.
Person pastes text into text box and pushes the generate button
Program takes the contents of the text box and parses it into a large array, removing only whitespace
Program then searches for patterns in the text and begins passing values into variables
I am trying to get the program to simply identify the pattern "Summary section collapse Name" because these four words should always be in this sequence. Once it identifies this it will pass the next two array values into first and last name variables. Here's some of the code:
var contactNameFirst, contactNameLast;
// Parse the input box into an array
var inputArr = document.getElementById('inputBox').value.split(/[\s]/);
for(var i = 0; i < inputArr.length; i++)
{
if(inputArr[i] == "Summary" && inputArr[i - 1] == "section" && inputArr[i - 2] == "Collapse" && inputArr[i + 1] == "Name")
{
if(inputArr[i + 2] != "Details")
{
contactNameFirst = inputArr[i + 2];
}
else
{
contactNameFirst = "";
}
if(inputArr[i + 3] != "Details")
{
contactNameLast = inputArr[i + 3];
}
else
{
contactNameLast = "";
}
}
}
document.getElementById('contactNameOutput').innerHTML = contactNameFirst + " " + contactNameLast;
Also, should I create a new post for this now, or keep editing this one?
Your accountLinesTemp is an Array of String, you could use the Array.sort function to sort your array as expected, and then use Array.join to get the full String if necessary.
See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort on MDN for more information.