I have a table of tasks where rows are grouped using ID's. I want to represent these groupings in another table where there is one row for each ID and all data rows and columns are grouped for each ID in a single cell. I need a formula that can group certain rows into a cell using newlines, and certain columns into cells using delimiters. My data source is something like this:
The formula needs to:
Filter rows by Condition.
Put Task ID's in their own column, one row for each individual ID.
Put all data in Hours, Mins, Customer and Description columns into a cell next to ID so that:
Hours and Mins are joined with a ":" as a delimiter to represent time values
Customer and Description values are then concatenated to time values using space as a delimiter.
The result should look like:
A Google Sheet containing example of the source table and desired result:
https://docs.google.com/spreadsheets/d/14p_HYpfb7XlWhRRgo3JN7SPRerwEaB2A7Dz1KWuBgL8/edit#gid=299935838
SUGGESTION:
You can try this custom formula GROUPDATA using the script below. Just add it as a bound script on your spreadsheet file:
Script:
function GROUPDATA(data) {
var temp = "";
var filtered = [];
var container = [];
var col2 =[];
var final = [];
data.forEach(function(data) {
if(data[0] == true){
filtered.push(data[1])
container.push([data[1],data[2]+":"+data[3]+" "+data[4]+" "+data[5]])
}
});
const unique = (value,index,self) =>
{return self.indexOf(value) ===index;}
var unique_list=filtered.filter(unique);
for(i=0; i<unique_list.length; i++){
container.forEach(function(x) {
if(x[0]== unique_list[i]){
temp = temp+ x[1]+"\n";
}
});
col2.push(temp);
temp = "";
}
for(y=0; y<unique_list.length; y++){
final.push([unique_list[y],col2[y]]);
}
return final;
}
DEMONSTRATION:
Used the custom formula =GROUPDATA(A:F) on cell G14
I am trying to write a formula that counts the number of times the number 1 appears in cell F1 of all my sheets. My sheets have varying names (18-0100, 18-0101, 18-0102...). I tried the following formula:
=COUNTIF(INDIRECT("'"&"'!F1"),"=1")
It acts unpredictably. It will only return 1 even if it should be more than 1. And when I try to start trying to count 2 instead of 1 it returns 0 and not the correct number.
What am I doing wrong?
Your formula counts only the current sheet.
To get them all you need to enter all sheet names:
The formula for each sheet is:
=(INDIRECT("'"& sheet_name &"'!F1")=1)*1
You can leverage a Google Apps Script to pull this off as well.
From your spreadsheet, go to Tools > Script Editor. Add the code below, then save. The function saved there will be accessible by all other Google apps, including your spreadsheet. You can then use the name of the function in your spreadsheet like you would with any other formula.
function OccurrencesInSheetNames(str) {
var c = 0;
var regExp = new RegExp('[' + str + ']', 'g');
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var sheetnames = [];
for (var i=0; i<sheets.length; i++) {
sheetnames.push([sheets[i].getName()]);
}
for (var i=0; i<sheetnames.length; i++) {
var name = sheetnames[i].toString();
var count = (name.match(regExp) || []).length;
c += count;
}
return c;
}
Now in your cell F1, place the following formula:
=OccurrencesInSheetNames(TEXT(1,0))
You can also replace the 1 in the above formula with a cell reference, if that works better for your needs. (e.g. =OccurrencesInSheetNames(TEXT(C5,0)) if you want to search through your sheet names for the integer number found in cell C5.)
Here's a demo spreadsheet for you to try out.
I have a complex Google Sheet query that works great except when a Google Sheet doesn't have as many columns as I use in my formula.
Here's what the formula looks like now:
=sum(filter(query(INDIRECT("'" & A2 & "'!$A$7:$23"),"select Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH where B='"&C2&"'",0),query(INDIRECT("'" & A2 & "'!$A$7:$23"),"select Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH where B='PROJECT'",0) >=date(2017,1,1),query(INDIRECT("'" & A2 & "'!$A$7:$23"),"select Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH where B='PROJECT'",0) <=date(2017,12,31)))
It works great. But the problem is I run it against many worksheets and some don't have e.g. column AG,AH and end at AF at which point I get an error.
So what I need is a way to generate the string Q,R,S....[Name of Last Column in Sheet] and then I can use that instead of my hard-coded Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH but I cannot figure out how to do that.
Any help is greatly appreciated. Thanks!
Per comments above, final formula was:
LEFT("Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC ",2*Columns(INDIRECT(A2&"!1:1"))-33+IF(Columns(INDIRECT(A2&"!1:1"))>26,Columns(INDIRECT(A2&"!1:1"))-26,0))
where column A contains the list of worksheets (tabs) in the Google Sheet. Put this in B2, and then copied it down. I am not marking this as the correct answer since others gave a correct formula-based answer but this did the trick for me.
This can be done with built-in functions:
On a helper sheet, let say you name it, helper, fill up range with letters A to Z, let say A1:A26
Let say that on B1 you write the following formula:
=ArrayFormula({A1:A26;TRANSPOSE(SPLIT(JOIN(",",SUBSTITUTE(QUERY(TRANSPOSE(A1:A26)&A1:A26,,27)," ",",")),","))}) . This will create a list of column letter headers.
On each new worksheet use columns(1:1) to get the total number of columns.
To get your string of column headers, then you could use something like :
JOIN(",",OFFSET(helper!B1,16,0,columns(1:1)-16))
QUERY(helper!B:B,"select B limit "&columns(1:1)-7&" offset 7")
NOTE:
If you decide to have only one helper sheet and use it on several spreadsheets, then use
QUERY(IMPORTRANGE(your_url,"helper!B:B"),"select Col1 limit "&columns(1:1)-7&" offset 7")
This can be done with script. Without seeing you spreadsheet, it is hard to know exactly what you need, but this should be close. I get the variables from Sheet1 and return the formula to Sheet1. Adjust the sheet name to fit your needs. This will look at your data sheets based on the variable sheet name determine the last column. Determine the column letters and build the string the query needs. It then sets the new query formula. I added a menu to run it from.
function onOpen() {
SpreadsheetApp.getActiveSpreadsheet().addMenu(
'Create Data', [
{ name: 'Run', functionName: 'formula' },
]);
}
function formula(){
var ss= SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getSheetByName("Sheet1") //sheet where variables are
var sheet=s.getRange("A2").getValue()//variable sheet name
var sel=makeString(sheet) //get the select string of column letters
//Create formula and return to Sheet1 A3
var f= s.getRange("A3").setFormula('=sum(filter(query(INDIRECT("\'" & A2 & "\'!$A$7:$23"),"select '+sel+' where B=\'"&C2&"\'",0),query(INDIRECT("\'" & A2 & "\'!$A$7:$23"),"select '+sel+' where B=\'PROJECT\'",0) >=date(2017,1,1),query(INDIRECT("\'" & A2 & "\'!$A$7:$23"),"select '+sel+' where B=\'PROJECT\'",0) <=date(2017,12,31)))')
}
function makeString(sht){
var ss= SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getSheetByName(sht)
var lc=s.getLastColumn()
var rng=s.getRange(1, 17, 1, lc).getValues()
var str=''
var ltr=[]
for(var i=17;i<rng[0].length+1;i++){
ltr[i]= columnToLetter(i)
str=ltr.join(',')
}
var str1=str.substr(17)
return str1
}
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
Let me know if you have any questions.
1) Range Across All Sheets:
I've googled everything but nothing. Basically, I need a formula that looks for the same range across all sheets.
My current formula looks like this:
=COUNTIF(Aug_15!$G:$G, "Shaun")+countif(July_15!$G:$G, "Shaun)+countif(June_15!$G:$G, "Shaun")+countif(May_15!$G:$G, "Shaun")+COUNTIF(Apr_15!$G:$G, "Shaun")+COUNTIF(Mar_15!$G:$G, "Shaun")
The issue I have is, as a month passes, a new sheet for the month is created. So this lowers the automation dramatically as you have to edit the formula every month. I'm basically looking for something that will search G:G across all sheets for that criteria.
So in my imaginary world, it would look something like this:
=COUNTIF(ALLSHEETS!$G:$G, "Shaun")
2) Cell Reference as Criterion
I'm trying to make the criteria look for something from another cell. For example, I'd replace "Shaun" with the cell L3. But it doesn't work! It searches for literally the two characters L and 3!
Is there anyway to make the criteria a value from another cell?
Many Thanks,
Shaun.
As Akshin Jalilov noticed, you will need a script to achieve that. I happen to have written a custom function for that scenario some time ago.
/**
* Counts the cells within the range on multiple sheets.
*
* #param {"A1:B23"} range The range to monitor (A1Notation).
* #param {"valueToCount"} countItem Either a string or a cell reference
* #param {"Sheet1, Sheet2"} excluded [Optional] - String that holds the names of the sheets that are excluded (comma-separated list);
* #return {number} The number of times the item appears in the range(s).
* #customfunction
*/
function COUNTALLSHEETS(range, countItem, excluded) {
try {
var count = 0,
ex = (excluded) ? Trim(excluded.split()) : false;
SpreadsheetApp.getActive()
.getSheets()
.forEach(function (s) {
if (ex && ex.indexOf(s.getName()) === -1 || !ex) {
s.getRange(range)
.getValues()
.reduce(function (a, b) {
return a.concat(b);
})
.forEach(function (v) {
if (v === countItem) count += 1;
});
};
});
return count;
} catch (e) {
throw e.message;
}
}
function Trim(v) {
return v.toString().replace(/^\s\s*/, "")
.replace(/\s\s*$/, "");
}
You can use the custom function in your spreadsheet like this:
=COUNTALLSHEETS("B2:B10", "Shaun")
or when 'Shaun' is in C2
=COUNTALLSHEETS("B2:B3", C2)
There is an optional parameter allowing you to provide a string with comma-separated sheet names you wish to exclude from the count. Don't use this paramater if you want to count ALL sheets.
See if that works for you ?
1) Range Across All Sheets:
The only way you can do that is via script, otherwise Spreadsheet functions cannot dynamically read sheets in the spreadsheet.
2) Cell Reference as Criterion
If the value of L3 is "Shaun" you can do this:
=COUNTIF(Aug_15!$G:$G, L3)
Make sure that you don't put L3 in quotes.
https://docs.google.com/spreadsheets/d/1wlWqdFwgv90s50iP-bXXBHciyualohj610qFiSatcmQ/edit?usp=sharing
Shared the above sheet with example. I want an arrayformula to sum the previous rows column and current row column.
In F2 I entered this formula:
=ArrayFormula(if(row(B2:B)=2, B2, if(len(B2:B)+len(C2:C), mmult(transpose(if(transpose(row(B2:C))>=row(B2:C),if(B2:B<>"", B2:B,0)+(if(C2:C<>"", C2:C,0)*-1), 0)),row(B2:B)^0),)))
But.. in order for this formula to work you will have to write your 'starting' balance in B2. See also the spreadsheet you shared.
I hope that helps ?
Try doing it via Script.
function calcBalance(range,InitialBalance){
var total = [];
total[0] = InitialBalance;
for (var i=2;i<range.length;i++) {
if (range[i][0] !== "") {
total[i-1] = range[i][0]-range[i][1]+total[i-2];
}
}
total = total.slice(1);
return (total);
}
In cell D3 type
=calcBalance(B:C,D2)
Hope this helps