So I know that empty cells are the worst for generating pivot tables however I have a huge csv that is generated like this:
ID
QTY
ITEM
DATE
800170
1
Donut
5/21/2022
800170
1
Bun
800170
1
Cake
800169
1
Sandwich
5/20/2022
800169
1
Cake
800169
2
Donut
800168
1
Donut
5/21/2022
800168
1
Cookie
800168
1
Tea
800167
1
Donut
5/22/2022
800167
1
Tea
and this is the pivot table that gets generated from it.
I am wondering if there is a way to have the dates "merged" by ID as an ID will always have the same Date?
Desired Output:
Here is a link to my test google sheet: https://docs.google.com/spreadsheets/d/1Loe3dCe4jqj14ZD7alYkb0IhkysOpArk5ZORtIahjdk/edit?usp=sharing
Unfortunately, the Pivot table has no function that will merge the data based on the ID. What you can do is to populate the date column of your raw data.
Here I created a script that will populate the data based on the previous value.
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('Fill Dates', 'fillDates').addToUi()
}
function fillDates(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheetByName("Sheet1");//Change this to your sheet name
var dLastRow = ss.getRange("D"+(ss.getLastRow()+1)).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
var startRow = 2;
var dateCol = 4
var range = ss.getRange(startRow, dateCol, dLastRow, 1);
var data = range.getValues();
var temp = '';
data.forEach(date =>{
if(date[0] != ''){
temp = date[0];
}else{
date[0] = temp
}
})
range.setValues(data)
}
To go to Apps Script, select Extensions > Apps Script. Copy paste the code above, save the script and refresh your spreadsheet. The script will create a custom menu in your spreadsheet and you can click that to run the script.
Demo:
Output:
Let me know if you have any issues or questions.
References:
Extending Google Sheets
Apps Script Spreadsheet Service
try:
=ARRAYFORMULA(QUERY({A2:C, VLOOKUP(ROW(D2:D), IF(D2:D<>"", {ROW(D2:D), D2:D}), 2)},
"select Col1,sum(Col2) where Col2>0 group by Col1 pivot Col4"))
or if you want totals:
=ARRAYFORMULA({QUERY({A:C, VLOOKUP(ROW(D:D), IF(D:D<>"", {ROW(D:D), D:D}), 2)},
"select Col1,sum(Col2) where Col2>0 group by Col1 pivot Col4"),
QUERY({A:B}, "select sum(Col2) where Col2>0 group by Col1 label sum(Col2)'Grand Total'");
{"Grand Total", TRANSPOSE(MMULT(TRANSPOSE(QUERY(QUERY({A:C,
VLOOKUP(ROW(D:D), IF(D:D<>"", {ROW(D:D), D:D}), 2)},
"select sum(Col2) where Col2>0 group by Col1 pivot Col4"), "offset 1", )*1),
SEQUENCE(COUNTUNIQUE(A2:A), 1, 1, ))), SUM(B:B)}})
or if you really love pivot table design:
demo sheet
Related
I have a sheets query that almost does what I want but I need a bit of help to get to the last step.
=QUERY(Sales!$A$2:$C,"SELECT B, SUM(C)
WHERE A='"&B3&"'
GROUP BY B
ORDER BY SUM(C) DESC
LIMIT 3
LABEL SUM(C) ''
FORMAT SUM(C) '$##,##0' "
,0)
This gives me the result in C3:D4. What I want is in E3.
I have two goals. First output the data stacked and joined like in cell E3. Second, the ideal solution is an array formula in C3 that does this for all the 'Partners'.
Sample Data
As always thanks in advance for the assistance and education!
Here is how I solved it. Maybe not the optimal solution because I need to duplicate the calculation, but it does exactly what I need. I've added my solution to the example data.
=REGEXREPLACE(REGEXREPLACE(textjoin(" ♦ ",0,
QUERY({Sales!$A$2:$A,Sales!$B$2:$B,Sales!$C$2:$C},"SELECT Col2, SUM(Col3)
WHERE Col1='"&B3&"'
GROUP BY Col2
ORDER BY SUM(Col3) DESC
LIMIT 3
LABEL SUM(Col3) ''
FORMAT SUM(Col3) '$##,##0' "
,0)
)," ♦ \$"," = \$")," ♦ ",CHAR(10))
One way to accomplish this is creating a custom function in Google Apps Script. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project:
function GET_TOP_CUSTOMERS(partners) {
const sheet = SpreadsheetApp.getActive().getSheetByName("Sales");
const data = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3).getValues();
return partners.map(row => row[0]).map(partner => {
const partnerRows = data.filter(row => row[0] === partner);
const uniqueCustomers = [...new Set(partnerRows.map(row => row[1]))];
const topCustomers = uniqueCustomers.map(customer => {
return [customer, partnerRows.filter(row => row[1] === customer)
.reduce((acc, current) => {
return acc + current[2] }, 0)];
}).sort((a,b) => b[1] - a[1]).slice(0, 3);
return topCustomers.map(customer => customer[0] + "; $" + customer[1].toFixed())
.join("\n");
});
}
Now, if you go back to your spreadsheet, you can use this function like any in-built one. You just have to provide the appropriate range (in this case it would be B3:B12), as you can see here:
Reference:
Custom Functions in Google Sheets
I've the below formula using ImportRange and Query along with Join and Split working correctly:
=join(" / ", QUERY(IMPORTRANGE("Google-Sheet-ID","RawData!A:AC"),"select Col25 where Col1 = " & JOIN(" OR Col1 = ", split(V2:V,"+")), 0))
Also, I've the below ArrayFormula with Split function working smoothly:
=ARRAYFORMULA(if(len(V2:V)=0,,split(V2:V,"+")))
But When I tried combining them together using the below formula:
=ARRAYFORMULA(if(len(V2:V)=0,,join(" / ", QUERY(IMPORTRANGE("Google-Sheet-ID","RawData!A:AC"),"select Col25 where Col1 = " & JOIN(" OR Col1 = ", split(V2:V,"+")), 0))))
It failed, and gave me the below error:
Error
Function SPLIT parameter 1 value should be non-empty.
Here is my sheet for your testing.
UPDATE
I changed it to:
=ARRAYFORMULA(if(len(C2:C)=0,,JOIN(" OR Col1 = ", ARRAYFORMULA(if(len(C2:C)=0,,split(C2:C,"+"))))))
So my full formula is:
=ARRAYFORMULA(
if(
len(C2:C)=0,,
join(" / ",
QUERY(
IMPORTRANGE("14iNSavtvjRU0XipPWIMKyHNwXTA85P_CafFTsIPHI6c","RawData!A:AC"),"select Col25 where Col1 = " &
ARRAYFORMULA(
if(len(C2:C)=0,,
JOIN(" OR Col1 = ",
ARRAYFORMULA(
if(
len(C2:C)=0,,split(C2:C,"+")
)
)
)
)
),
0
))))
And now getting the error:
Error
JOIN range must be a single row or a single column.
I believe this formula on the tab called MK.Testing will pull the info you're hoping for.
=QUERY(IMPORTRANGE("14iNSavtvjRU0XipPWIMKyHNwXTA85P_CafFTsIPHI6c","RawData!A:AC"),"select Col25 where Col1="&TEXTJOIN(" or Col1=",TRUE,A2:A))
I think you might have been overcomplicating things? This formula just forms a text string out of the shipment IDs to use in a query. one thing that may be tripping you up is that query() is very particular about the type of data in a column. Your shipment IDs can be numbers, or they can be number letter combos, but not both. That is, if you have some shipment IDs that contain letters and others that don't, it will be more difficult to get a query that works. (though not impossible). For the sake of helping you though, it's important that your sample IDs reflect the real ones in this way as accurately as possible.
How about doing this with Apps Script? You can get the values from the Sheet2, Shipment Ids, and the Ids from MK.Testing and compare them. If they coincide, the you copy the ETA into the Column C of MK. Testing:
function myFunction() {
var sprsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet2 = sprsheet.getSheetByName("Sheet2");
var mkTesting = sprsheet.getSheetByName("MK.Testing");
var shipmentId = sheet2.getRange("A2:A").getValues();
var idList = mkTesting.getRange("A2:A").getValues();
for (var i = 0; i < shipmentId.length; i++){
for (var j = 0; j < idList.length; j++){
if (idList[j][0] == ""){break;} //Stops if there is an empty cell in Mk.Testing's column A
if (idList[j][0] === shipmentId[i][0]){
var eta = sheet2.getRange("E"+(i+2)).getValue();
mkTesting.getRange("C"+(j+2)).setValue(eta);
}
}
}
}
References:
SpreadsheetApp Class
Range Class
I have a google sheet like this example to track scores for disc golf:
https://docs.google.com/spreadsheets/d/1uxDFXg2kivZWKICeVklugyXH1OWqsq_s5qXZYzgHkt8/edit?usp=sharing
It works great for tracking day to day scores but it would be really awesome to have a single sheet at the beginning that could say everyones total scores that they have gotten. Also note that the names may not be the same in each sheet.
So in this example I would want to have a new sheet that would automatically calculate the scores from the other sheets and show:
Mike 67,71,65
George 83,70
Phillip 79,72,65
John 66,71
Henry 69
I am very unfamiliar with excel formulas and have been struggling to get this started. Any help would be greatly appreciated.
You can use a Google Apps Script to accomplish what you are looking for. The idea of the code is that it will iterate over every Sheet in your Spreadsheet, gather all the players and all their values, and finally create a summary and put it into the "Summary" sheet (that sheet must exist in your Spreadsheet, with strictly the same name):
function updateSummary() {
var sheets = SpreadsheetApp.getActive().getSheets();
var summarySheet = SpreadsheetApp.getActive().getSheetByName('Summary');
var allScores = {};
for (var i=0; i<sheets.length; i++) {
if (sheets[i].getName() == 'Summary') continue;
var nColumns = sheets[i].getLastColumn();
var names = sheets[i].getRange(1, 1, 1, nColumns).getValues()[0];
var scores = sheets[i].getRange(20, 1, 1, nColumns).getValues()[0];
for (var j=0; j<nColumns; j++) {
var currentName = names[j];
var currentScore = scores[j];
if (!allScores.hasOwnProperty(currentName))
allScores[currentName] = [];
allScores[currentName].push(currentScore);
}
}
summarySheet.clear();
for (var key in allScores) {
var row = [key].concat(allScores[key]);
summarySheet.appendRow(row);
}
}
This will create, with the data given, the following data in the "Summary" Sheet:
Instead, if you prefer to have two columns as you described in your question (with the second one holding every score separated by commas), you would simply need to replace the last for-loop in the code above for the following one:
for (var key in allScores) {
var row = [key].concat(allScores[key].join(','));
summarySheet.appendRow(row);
}
Finally, you can create an image in the "Summary" sheet which can serve as a button to run the script. To do so:
Within your Sheet, click on Insert>Image>Image over cells.
Select any image of your choice.
Select the newly created image and click on the three dots icon that appears on the top-right corner of the image.
Click on "Assign script" and put the function name (in this case, updateSummary) and click on OK.
try:
=QUERY(TRANSPOSE({
'MikeGeorgePhillipJohn 121519'!A1:D20,
'MikeGeorgePhillipJohn 122019'!A1:D20,
'MikeJosephPhillipHenry 122719'!A1:D20}),
"select Col1,sum(Col20)
where Col1 is not null
group by Col1
label sum(Col20)''", 0)
=ARRAYFORMULA(SPLIT(TRANSPOSE(QUERY(QUERY(TRANSPOSE({
'MikeGeorgePhillipJohn 121519'!A1:D20,
'MikeGeorgePhillipJohn 122019'!A1:D20,
'MikeJosephPhillipHenry 122719'!A1:D20}),
"select max(Col20)
where Col1 is not null
group by Col20
pivot Col1", 0),,999^99)), " "))
I want to query data of multiple sheets in the chronological order in which data is being entered in the sheet.
=QUERY(
{IMPORTRANGE("1vIMYCHnj-jS_bIzfenLWcBTLdLiIJJ576p-0nk9tvto", "Sheet1!A1:C");
IMPORTRANGE("1UEaMB75MASF5vuIjWcsRN7UjZrbg2la74Y6MbW1jBbA", "Sheet1!A2:C")},
"where Col1 is not null order by Col3", 1)
for timestamp you need this script:
function onEdit(e) {
var s = SpreadsheetApp.getActiveSheet();
{
var r = s.getActiveCell();
if( r.getColumn() == 2 ) {
var nextCell = r.offset(0, 1);
var newDate = Utilities.formatDate(new Date(),
"GMT+1", "dd/MM/yyyy hh:mm:ss");
nextCell.setValue(newDate);
}
}
how to add a script to your spreadsheet
go to Tools
select Script editor
copy paste the script
save the project under some name
click on run icon and authorise it...
select your account
click on Advanced
select Go to * (unsafe)
click on Allow and return to your sheet (you can close script window/tab)
I have a column with a bunch of ingredients lists in it. I'm trying to figure out how many times different individual ingredients appear. There are 73,000 rows. The answers on this question works for a small amount of data in Google Sheets.
Formula is =UNIQUE(TRANSPOSE(SPLIT(JOIN(", ";A2:A);", ";FALSE)))
But I've overwhelmed JOIN with more than 50000 characters here. Is there another way to tackle this?
Sheet: https://docs.google.com/spreadsheets/d/1t0P9hMmVpwhI2IbATmIMjobuALTg8VWhl8-AQaq3zIo/edit?usp=sharing
=ARRAYFORMULA(UNIQUE(TRIM(TRANSPOSE(SPLIT(TRANSPOSE(
QUERY(","&A1:A,,5000000)),",")))))
=QUERY(QUERY(ARRAYFORMULA(TRIM(TRANSPOSE(SPLIT(TRANSPOSE(
QUERY(","&A1:A,,5000000)),",")))),
"select Col1,count(Col1)
where Col1 is not null
group by Col1
label count(Col1)''"),
"order by Col2 desc")
demo spreadsheet
=UNIQUE(TRANSPOSE(SPLIT(REGEXREPLACE(TRANSPOSE(
QUERY(ARRAYFORMULA(","&A1:A),,5000000))," ,",","),",")))
but maybe you need this (?):
=QUERY(TRANSPOSE(SPLIT(REGEXREPLACE(TRANSPOSE(
QUERY(ARRAYFORMULA(","&A1:A),,5000000))," ,",","),",")),
"select Col1,count(Col1)
where Col1 is not null
group by Col1
label count(Col1)''")
I did a google scripting solution because I wanted to play with key map pairs.
function myFunction() {
var myMap = {"candy":0};
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheetByName("FIRSTSHEETNAME");
var os = sh.getSheetByName("Ingredients");
var data = ss.getDataRange().getValues();
for (var i=0; i<data.length;i++)//full
//for (var i=1; i<4000;i++)//test
{
var array = data[i][0].split( ",");
for (var j=0; j<array.length;j++)
{
var item = array[j];
//Logger.log(array[j]);
if (myMap[item]>-1){
//Logger.log("REPEAT INGREDIENT");
var num = parseInt(myMap[item]);
num++;
myMap[item]=num;
//Logger.log(item +" "+num);
} else {
myMap[item]=1;
//Logger.log("New Ingredient: "+item);
//Logger.log(myMap);
}
}
}
//Logger.log(myMap);
var output=[];
for (var key in myMap){
//Logger.log("Ack");
output.push([key,myMap[key]]);
}
//Logger.log(output);
os.getRange(2,1,output.length,output[0].length).setValues(output);
}
You'll need to add an "Ingredients" tab for the output and change your first tab to be called FIRSTSHEETNAME (or change the code). In my testing it took 4 seconds for 4 items, 5 seconds for 400 items, and 6 seconds for 4000 items. there might be an issue with leading spaces but this gives you a place to start.
A fast running formula that works with columns of at least 40,000 rows:
=query(arrayformula(TRIM(flatten(split(A2:A20000,",")))),"select Col1,Count(Col1) Where NOT (Col1='' OR Col1 contains '#VALUE!') Group By Col1 order by Count(Col1) desc label Col1 'Ingredient',Count(Col1) 'Freq.'")
FLATTEN function, combined with SQL (QUERY function) can be a solution for fast filtering of values (such as empty or error messages).
TRIM function avoids artifacts in the result due to meaningless spaces before/after each string.
Sheet: https://docs.google.com/spreadsheets/d/1m9EvhQB1Leg2H7L52WhPe66_jRrTc8VsnZcliQsxJ7s/edit?usp=sharing
*In case of false case differences, you could normalize all characters of the strings to uppercase before within the same formula with UPPER(A2:A20000).