I am trying to consolidate data from multiple tabs into a consolidated sheet. Each tab is an individual form and has the same format. On the consolidated sheet, I want to re-arrange the data so the data field name is in a column, and data values are in rows. I tried the following:
function consolidateData(){
// defined all variables
var sheetNames = [];
var dataSheet = [];
var dataValues = [];
var conso=[];
var header = [["Faculty Name","Faculty ID","Date of Joining"]];
var ws = SpreadsheetApp.getActiveSpreadsheet();
// get all sheets
var allsheets = ws.getSheets();
for(var s in allsheets)
var sheet = allsheets[s];
sheetNames[s] = sheet.getName();
dataSheet[s] = ws.getSheetByName(sheetNames[s]);
// writing data into new sheet
var newSheet = ws.insertSheet().setName("Consolidated_Data");
newSheet.getRange("A1:C1").setValues(header);
var name = dataSheet[s].getRange("B1").getValue();
var id = dataSheet[s].getRange("B3").getValue();
var doj = dataSheet[s].getRange("B5").getValue();
var faculty = [(name),(id),(doj)];//convert into array
var facultycount = faculty.length;
for (var i = 0; i < faculty.length; i++)
//Loop through all rows and write them out starting on ROW2
{
newSheet.getRange(2 + i, 1).setValue(faculty[0]);//
newSheet.getRange(2 + i, 2).setValue(faculty[1]);//
newSheet.getRange(2 + i, 3).setValue(faculty[2]);//
}
}
There are four tabs and I expect to see results from each tab in the Consolidated_Data tab. But I only saw the last tab data got inserted repeatedly. Can anyone help? Thank you. Consolidated Data Sheet Example of an individual tab
While traversing through all your sheets, you haven't used curly braces after the for loop -
for(var s in allsheets)
So it's running the loop and the value of s stays at the last index of allsheets.
However, might I suggest a simplified version I have tested out -
function consolidateData () {
const headers = ["Faculty Name", "Faculty ID", "Date of joining"];
const rows = { "name": 0, "id": 2, "doj": 4 };
const consolidatedSheetName = "Consolidated_Data";
const ss = SpreadsheetApp.getActive();
const sheets = ss.getSheets();
let consolidatedValues = [];
// Setting headers
consolidatedValues.push(headers);
// Fetching values
for(let sheet of sheets) {
if(sheet.getName()==consolidatedSheetName) { continue; }
let data = sheet.getRange("B1:B5").getValues();
let faculty = [ data[rows.name][0], data[rows.id][0], data[rows.doj][0] ];
consolidatedValues.push(faculty);
}
// Adding to sheet
let consolidatedSheet = ss.getSheetByName(consolidatedSheetName);
if(!consolidatedSheet) {
consolidatedSheet = ss.insertSheet().setName(consolidatedSheetName);
}
let range = consolidatedSheet.getRange(1, 1, consolidatedValues.length, consolidatedValues[0].length); // 1, 1, 3, 3
range.setValues(consolidatedValues);
}
Issue:
The script is overwriting the same range here:
newSheet.getRange(2 + i, 1).setValue(faculty[0]);
Solution:
Add 1 for each row added:
newSheet.getRange(2 + i + s, 1).setValue(faculty[0]);
Or use sheet#appendRow()
newSheet.appendRow(faculty);
If you practice best practices, Your script can be simplified like this:
const consolidate = () => {
const ws = SpreadsheetApp.getActiveSpreadsheet(),
oldSheets = ws.getSheets(),
newSheet = ws.insertSheet().setName('Consolidated_Data');
newSheet.getRange(1, 1, 1 + oldSheets.length * 3, 3).setValues([
['Faculty Name', 'Faculty ID', 'Date of Joining'],
...oldSheets.map(sheet =>
sheet
.getRange('B1:B5')
.getValues()
.reduce((a, c, i) => (i % 2 === 0 ? [...a, c[0]] : a), [])
),
]);
};
Related
I have about many spreadsheet work with my partners and each partner use 1 spreadsheat for manage data.
I using a table to store all ID, Sheet name and column store data I need to import to my master sheet.
This is my Spreadsheet:
Link here
I using Appscript to do import data by read values in this table with 2 loop, I see it work to slow How could I speed up it?
this is my script
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Source Link');
var master = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master') ;
const dstLr = getLastPopulatedRow(ss); //get last row of Source Link
const target_sheet_name = ss.getRange(2,6,dstLr-1,1).getValues(); //Get data in Column F input to array
const ids = ss.getRange(2,1,dstLr-1,1).getValues(); //get data in column A input to array
const ebayID = ss.getRange(2,2,dstLr - 1,1).getValues(); //get cloumn index at Column B input to array
const team = ss.getRange(2,3,dstLr-1,1).getValues(); //get column containt team name at column C input to array
const linkOrder = ss.getRange(2,4,dstLr-1,1).getValues(); //get cloumn containt Link Order at column D input to array
const tracking = ss.getRange (2,5,dstLr-1,1).getValues(); //get column containt Tracking at column E input to array
const benCO = ss.getRange(2,7,dstLr,1).getValues();
ids.forEach((id, i) => {
const srcSheet = SpreadsheetApp.openById(id).getSheetByName(target_sheet_name[i]);
const lr = getLastPopulatedRow(srcSheet);
const srcCol = [ebayID[i], team[i], linkOrder[i],tracking[i]].flat();
for(var j = 0; j < srcCol.length; j++) {
const destCol = [1,2,3,4];
const destCol2 = [5];
destCols = destCol[j]
const srcRange = srcSheet.getRange(3, srcCol[j], lr); // Origin range to copy
const values = srcRange.getValues(); // Getting values from origin column
const destRange = master.getRange(2, destCols, lr);
master.clearContents;
destRange.setValues(values);
}
})
}
function getLastPopulatedRow(sheet) {
var data = sheet.getDataRange().getValues();
for (var i = data.length-1; i > 0; i--) {
for (var j = 0; j < data[0].length; j++) {
if (data[i][j]) return i+1;
}
}
return 0;
}
I also need add value of G column for each data I import from ID and Sheet name at colum A and column F, but when I try set value for column E at Master sheet I got error that "data have 1 but destination range have 582"
Could Some one give me advise
That's what I try
ids.forEach((id, i) => {
const srcSheet = SpreadsheetApp.openById(id).getSheetByName(target_sheet_name[i]);
const lr = getLastPopulatedRow(srcSheet);
const srcCol = [ebayID[i], team[i], linkOrder[i],tracking[i]].flat();
for(var j = 0; j < srcCol.length; j++) {
const destCol = [1,2,3,4];
const destCol2 = [5];
destCols = destCol[j]
const srcRange = srcSheet.getRange(3, srcCol[j], lr); // Origin range to copy
const values = srcRange.getValues(); // Getting values from origin column
const destRange = master.getRange(2, destCols, lr);
master.clearContents;
destRange.setValues(values);
}
const coName = [];
coName.push(benCO[i]);
master.getRange(2,5,lr,1).setValues(coName);
})
and this is error:
From your question and samples, how about the following sample script?
Sample script:
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sourceLink = ss.getSheetByName('Source Link');
const master = ss.getSheetByName('Master');
const srcValues = sourceLink.getRange("A2:G" + sourceLink.getLastRow()).getValues();
const header = ["Order ID", "Team", "ID", "ID TRACKING#"];
const values = [header, ...srcValues.flatMap(r => {
const sheet = SpreadsheetApp.openById(r[0]).getSheetByName(r[5]);
const [, h, ...v] = sheet.getDataRange().getValues();
const temp = h.map(e => e.trim());
const indexes = header.map(e => temp.indexOf(e));
return v.map(r => indexes.map(i => r[i]));
})];
master.clearContents().getRange(1, 1, values.length, values[0].length).setValues(values);
}
In this sample script, first, the header is declared and the values are retrieved using the header from each sheet. And, the populated values are put into the destination sheet.
In this sample script, the header is used from your sample Spreadsheet. So, when you change the header titles, the result values might not be able to be used. Please be careful about this.
Reference:
map()
Added:
From your following reply,
I got messenger error at this line: const temp = h.map(e => e.trim()); messenger said: TypeError: e.trim is not a function
My proposed script is for your provided Spreadsheet, and when I tested my proposed script no error occurs. From your reply, I guessed that your Spreadsheet might be different from your provided Spreadsheet. If my understanding is correct, from your error message, I'm worried that the header rows might be changed. In your provided Spreadsheet, 1st 2 rows are header rows. But, if the header row is only the 1st row, the error might occur because the row values include the date object in the 2nd row. If my understanding is correct, can you test the following sample script?
Sample script:
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sourceLink = ss.getSheetByName('Source Link');
const master = ss.getSheetByName('Master');
const srcValues = sourceLink.getRange("A2:G" + sourceLink.getLastRow()).getValues();
const header = ["Order ID", "Team", "ID", "ID TRACKING#"];
const values = [header, ...srcValues.flatMap(r => {
const sheet = SpreadsheetApp.openById(r[0]).getSheetByName(r[5]);
// const [, h, ...v] = sheet.getDataRange().getValues();
let h;
let [h1, h2, ...v] = sheet.getDataRange().getValues();
if (h2.some(e => e instanceof Date)) {
h = h1;
v = [h2, ...v];
} else {
h = h2;
}
const temp = h.map(e => e.trim());
const indexes = header.map(e => temp.indexOf(e));
return v.map(r => indexes.map(i => r[i]));
})];
master.clearContents().getRange(1, 1, values.length, values[0].length).setValues(values);
}
If the same error occurs and another error occurs, can you provide the sample Spreadsheet for correctly replicating the issue? By this, I would like to confirm it.
This question already has answers here:
Transposing a 2D-array in JavaScript
(25 answers)
Closed 7 months ago.
I'm trying to grab the data for my spreadsheet for the rows and headers based on a unique identifier. I can grab the data easily enough, put it into an array, but I cannot figure out how to transpose the data into a column. tried to sanitize so forgive if i missed something.
test sheet:
https://docs.google.com/spreadsheets/d/195YJwCj5KUljVnIldooQ-1DMEvw1b4vPj6-9Hc1xgJA/edit?usp=sharing
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var uid = "2" //will eventually be dynamic, testing
var allData = sheet.getDataRange().getValues();
var temp = [];
var dataTemp = []
var uidList = allData.getRange(1, 2, allData.getLastRow(), 1).getValues().map(function (r) { return r[0] })
var uidIndex = uidList.indexOf(uid)
var rowData = []
var headers = []
allData[0].forEach(function (r) { headers.push(r) })
allData[0].forEach(function (x, i) {
rowData.push(allData[uidIndex][i])
})
var combined = []
combined.push(headers)
combined.push(rowData)
combined[0].forEach((x, i) => { //grab headers
temp.push(combined[i][0])
});
combined[1].forEach((x, i) => { //grab data row
dataTemp.push(combined[i][1])
});
// sheet.getRange(3,1,combined[0].length,combined.length).setValues(temp)
}
You can use this function, you have to pass your sheet for parametres and this function will transpose all google sheets.
function transposeSheet(sheet){
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
//Defining a blank array that can hold the result
trans = [];
//transpose the data stored in range variable
for(var column = 0; column < range[0].length; column++){
trans[column] = [];
for(var row = 0; row < range.length; row++){
trans[column][row] = range[row][column];
}
}
//Remove current data
sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).clear();
//printing values stored into trans variable on spreadsheet range
sheet.getRange(1, 1, trans.length, trans[0].length).setValues(trans);
}
Goodday,
I have 2 sheets. For each ID on sheet1 I need to verify if that ID is also on sheet2 AND if Date Processed is blank.
If both condition are true > today's date should be set in Date Processed.
I've managed to do so for just 1 value (A2) from sheet1.
What I need is a way to go through all the values in sheet1. Ideally the row in sheet1 would also get deleted (not sure if that is possible)
This is my code till now
function myMatch(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var ss = file.getSheetByName("Sheet1");
var ws = file.getSheetByName("Sheet2");
var wsData = ws.getDataRange().getValues();
var mySearch = ss.getRange("A2").getValue();
for(var i = 0; i < wsData.length; i++){
if(wsData[i][1] == mySearch && wsData[i][2] == "")
{
ws.getRange(i+1,3).setNumberFormat('dd"-"mmm"-"yyyy').setValue(new Date());
}
}
}
Your help is really appreciated as I have been trying and searching for a solution for 2 days now. Thank you
I know it doesn't makes much sense. Muhammet's code works and looks just fine. But, rather for fun and educational purposes, here is another "functional" solution:
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const s1 = ss.getSheetByName('Sheet1');
const s2 = ss.getSheetByName('Sheet2');
// get data from Sheet1 and Sheet2
const s1_data = s1.getDataRange().getValues().slice(1,);
const s2_data = s2.getDataRange().getValues().slice(1,);
// get IDs from data of Sheet1
const IDs = s1_data.map(x => x[0]);
const IDs_to_delete = []; // here will be IDs to delete
// function checks and fill a row and adds ID to array to delete
const write_date = (id,row) => {
if (row[1] == id && row[2] == '') {
IDs_to_delete.push(id);
row[2] = new Date();
}
}
// change rows within data of Sheet 2
IDs.forEach(id => s2_data.forEach(row => row = write_date(id,row)));
// clear and fill Sheet 2
s2.getDataRange().offset(1,0).clearContent();
s2.getRange(2,1,s2_data.length,s2_data[0].length).setValues(s2_data);
// remove rows from data of Sheet 1
const s1_data_new = s1_data.filter(row => !IDs_to_delete.includes(row[0]));
// clear and fill Sheet 1 with new data
s1.getDataRange().offset(1,0).clearContent();
s1.getRange(2,1,s1_data_new.length,s1_data_new[0].length).setValues(s1_data_new);
}
The only improvements over Muhamed's code is that this implementation removes processed rows from Sheet1. And, I believe, it will work faster for huge lists, because it doesn't use getRange() and setValue() on every found cell but fills all cells of the sheet at once with setValues() method.
You need a loop for this. Use
var mySearchs = ss.getRange('A2:A').getValues();
and loop through all values of this array.
function myMatch(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var ss = file.getSheetByName("Sheet1");
var ws = file.getSheetByName("Sheet2");
var wsData = ws.getDataRange().getValues();
var mySearchs = ss.getRange('A2:A').getValues();
mySearchs.forEach((v) => {
for(var i = 0; i < wsData.length; i++){
if(wsData[i][1] == v && wsData[i][2] == "")
{
ws.getRange(i+1,3).setNumberFormat('dd"-"mmm"-"yyyy').setValue(new Date());
}
}
})
}
Hello I am trying to have a continual list that copies values from one sheet to another in google sheets. My problem is that it is overwriting on the second sheet. I can't figure out how to find the last row before it inserts values. Here is my code.
function SubmitData() {
var s = SpreadsheetApp.getActiveSpreadsheet();
var sht = s.getSheetByName('Validation')
var drng = sht.getDataRange();
var rng = sht.getRange(6,2, drng.getLastRow(),drng.getLastColumn());
var rngA = rng.getValues();//Array of input values
var rngB = [];//Array where values that past the condition will go
var b = 0;//Output iterator
for(var i = 0; i < rngA.length; i++)
{
rngB[b]=[];//Initial new array
rngB[b].push(rngA[i][0],rngA[i][2]);
b++;
}
var shtout = s.getSheetByName('Track Data');
var outrng = shtout.getRange(2,1, rngB.length,2);//Make the output range the same size as the output array
outrng.setValues(rngB);
I don't see any condition in your code, you're just remapping
function SubmitData() {
const s = SpreadsheetApp.getActive();
const sht = s.getSheetByName('Validation');
const shtout = s.getSheetByName('Track Data');
const rng = sht.getRange(6, 2, sht.getLastRow() - 5, sht.getLastColumn() - 1);
const vs = rng.getValues();
let vO = vs.map(r => [r[0], r[2]]);
shtout.getRange(shtout.getLastRow() + 1, 1, vO.length, vO[0].length).setValues(vO);
}
Here is a modified script, easier to read. If you have the "sheet" in a variable you can call the getLastRow() method. Then plus 1.
You also can push an array inside an array.
Be aware that bad written functions can plus the lastrow. The "" will be see ad NOT empty. For instance:
//BAD:
=IF(A10 = "","","This is not empty")
//GOOD:
=IF(A10 = "",,"This is not empty")
The script:
function SubmitData() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const inputSheet = ss.getSheetByName('Validation')
const inputValues = inputSheet.getRange(6,2, inputSheet.getLastRow(),inputSheet.getLastColumn()).getValues()
const output = [];
inputValues.forEach(row => {
output.push([row[0].row[2]])
})
const outputSheet = ss.getSheetByName('Track Data');
outputSheet.getRange(outputSheet.getLastRow() + 1,1,output.length,output[0].length).setValues(output)
}
I got the following table to populate (range D6:J15) as I search the data in another sheet, based on a date criteria found in row 4:
This is where I'm to look for the data, considering Col A as the basis for the criteria:
My difficulty is to concatenate the data, as they meet the criteria.
This is the code I'm working on:
/* #OnlyCurrentDoc */
function editarPrevProd() {
const lock = LockService.getScriptLock();
lock.tryLock(3000);
if (lock.hasLock()) {
var sourceSheet = 'PrevProdDB2';
var destinationSheet = 'Previsão Entreposto';
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sourceSheet);
var ActiveSheetName = ss.getActiveSheet().getName();
var LastRowSource = sheet.getLastRow();
var LastColumnSource = sheet.getLastColumn();
var values = sheet.getRange(2,1,LastRowSource,9).getValues();
var csh = ss.getSheetByName(destinationSheet);
var itens = csh.getRange("I40:J57");
var data = [];
var weekNo = csh.getRange("B4").getValue();
var weekDates = csh.getRange("D4:J4").getValues();
if (weekNo == "") {
Browser.msgBox("Escolher uma data e tente novamente!");
return;
}
//var clearRng = ["K34:K35", "N34:N35", "I40:K"];
//csh.getRangeList(clearRng).clearContent();
for (var i = 0; i < values.length; i++) {
if (values[i][7] == weekNo) {
data.push(values[i]);
//break;
}
}
var dias = 0;
var prevData = [];
for (var j = 0; j < weekDates.length; j++) {
dias = dias + 1;
Logger.log("Dias da Semana: " + dias);
for (var a = 0; a < data.length; a++) {
if (weekDates[j].valueOf() == data[a][0].valueOf()){
prevData.push(data[a][4]);
}
}
}
//map columns whose data will be set in the header.
var user = data.map(function(e){return e[5];});
var lastUpdate = data.map(function(e){return e[6];});
//Copy data array to destination sheet
csh.getRange("I1").setValue(user);
csh.getRange("I2").setValue(lastUpdate);
//csh.getRange("E6").setValue(timeStamp);
//If you wanted to set arrays in the form of
//a table, you'd use this below instead
var seg = data.map(function(e) {return [e[3]];});
var ter = data.map(function(e) {return [e[4]];});
var qua = data.map(function(e) {return [e[5]];});
var qui = data.map(function(e) {return [e[6]];});
var sex = data.map(function(e) {return [e[7]];});
var sab = data.map(function(e) {return [e[8]];});
var dom = data.map(function(e) {return [e[9]];});
//csh.getRange(6,4,data.length,1).setValues(seg);
lock.releaseLock();
}
}
Here's a sample of the file. Note that the gs file I'm working on is named SalvaPrevProducao.
https://docs.google.com/spreadsheets/d/1NOWkzQIAPPdZdxeeTR7Id2v8LR00_u06uPhHs3tzLuU/edit?usp=sharing
I believe your goal as follows.
You want to convert the above image to the bottom image using Google Apps Script.
The date header is the cells "D4:J4".
The source values are the cells "A6:M".
The column "M" of ID is Semana in the destination sheet.
In this case, I would like to propose the following flow.
Retrieve values from the source sheet.
Create an array for putting to the destination sheet.
Put the array to the destination sheet.
When this flow is reflected to the Google Apps Script, it becomes as follows.
Sample script:
Before you use this script, please set the variables of srcSheetName and dstSheetName.
function editarPrevProd() {
const srcSheetName = "Data Source"; // This is the source sheet name.
const dstSheetName = "destSheet"; // Please set the destination sheet name.
// This is from https://stackoverflow.com/a/44563639
Object.prototype.get1stNonEmptyRowFromBottom = function (columnNumber, offsetRow = 1) {
const search = this.getRange(offsetRow, columnNumber, this.getMaxRows()).createTextFinder(".").useRegularExpression(true).findPrevious();
return search ? search.getRow() : offsetRow;
};
// 1. Retrieve values from the source sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const srcSheet = ss.getSheetByName(srcSheetName);
const lastRow = srcSheet.get1stNonEmptyRowFromBottom(1);
const [[, , , ...header1], header2, ...srcValues] = srcSheet.getRange("A4:M" + lastRow).getValues();
// 2. Create an array for putting to the destination sheet.
const values = header1.reduce((ar, h, i) => {
srcValues.forEach(([a, b, c, ...dm]) => ar.push([h, a, b, c, dm[i] || 0, "", "", dm.pop(), h]));
return ar;
}, [["Data", "Tipo", "Cod", "Descrição", "Qtd", "Usuário", "TimeStamp", "Semana", "Data"]]);
// 3. Put the array to the destination sheet.
const dstSheet = ss.getSheetByName(dstSheetName);
dstSheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}
When above script is run, the values are retrieved from srcSheetName and the converted values are put to dstSheetName .
Result:
When above script is run, the following result is obtained.
Note:
Unfortunately, from your question and sample Spreadsheet, I couldn't understand about Usuário and TimeStamp of the columns "F" and "G". At the sample output situation of Turn the data from the left into the format on the right side, Usuário and TimeStamp have no values.
References:
reduce()
forEach()
It is unclear why you would need to resort to scripting to look up those values, when a filter() formula would seem capable to do the same. Try this formula in cell D6:
=sum( iferror( filter(PrevProdDB2!$E$2:$E, PrevProdDB2!$B$2:$B = $A6, PrevProdDB2!$H$2:$H = $B$4, PrevProdDB2!$I$2:$I = D$4) ) )