I have in a column the invoice info of a client (name, address, email, phone number) each one in one row of the column A, and I need to Copy it to another range in transpose style.
I used the following script trying to edit it as I need but I do not find how
function copyAndDeleteSalidas () {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var source = ss.getRange ("Sheet1!A1:A6");
var destSheet = ss.getSheetByName("Sheet2!");
var destRange = destSheet.getRange(destSheet.getLastRow()+1,1);
source.copyTo (destRange, {contentsOnly: true});
source.clear ();
}
You'll need a function for transposing 2D arrays, since it appears there isn't a built-in option for this purpose in Apps Script. There are several versions of such a function on Stack Overflow, I used one of them below.
The main difference from your script is that instead of copyTo, I use setValues and getValues. Also, the destination range now must have the size that matches the size of values inserted in it.
function copyTransposeAndDelete () {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var source = ss.getRange("Sheet1!A1:A6");
var destSheet = ss.getSheetByName("Sheet2");
var destRange = destSheet.getRange(destSheet.getLastRow()+1, 1, source.getWidth(), source.getHeight());
destRange.setValues(transpose(source.getValues()));
source.clear ();
}
function transpose(array) {
return array[0].map(function(col, i) {
return array.map(function(row) {
return row[i];
});
});
}
Related
I have a tracker to count daily productivity.
This is appended to a weekly tracker, in which the sum of the two serves as a running total.
The weekly tracker is appended to a separate sheet to serve as historical data.
TRACKER
DATA
As you can see in the 'DATA' screenshot, the formula is being copied from K12:O12
see function copyTT / source.copyTo
I want to retain cell formatting, and copy only the cell data, not it's formula.
Script;
function mergeTT() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var source = ss.getRange("NEW_TTRACKER!J3:O3");
var destSheet = ss.getSheetByName("NEW_TTRACKER");
var lastRow = destSheet.getRange("J7:J11").getValues().filter(String).length;
var destRange = destSheet.getRange(lastRow+7,10,1,6);
source.copyTo(destRange, {contentsOnly: true});
var ws = ss.getSheetByName("NEW_TTRACKER")
ws.getRange('K3:O3').clearContent();
}
function copyTT() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var source = ss.getRange("NEW_TTRACKER!J5:O12");
var destSheet = ss.getSheetByName("NEW_TDATA");
var destRange = destSheet.getRange(destSheet.getLastRow()+1,1);
source.copyTo(destRange, {contentsOnly: false});
var sheet = SpreadsheetApp.getActive().getSheetByName("NEW_TTRACKER");
sheet.getRange('J7:O11').clearContent();
}
Thanks in advance.
I've been attempting to get around it playing with different CopyPasteType properties and I'm really not getting anywhere.
About I've been attempting to get around it playing with different CopyPasteType properties, I think that your approach is correct. In your script, how about modifying your script as follows?
From:
source.copyTo(destRange, {contentsOnly: true});
To:
source.copyTo(destRange, { contentsOnly: true });
source.copyTo(destRange, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false); // Added
By this modification, the format is also copied.
Reference:
copyTo(destination, copyPasteType, transposed)
Is it possible to copy values after applying a filter?
I want to ignore the hidden values.
I need to filter a sheet with more than 2000 rows and if I use a loop it takes a long time.
Then, I use this:
var filteredRangefec = range.createFilter()
.setColumnFilterCriteria(6,filterCriteria)
.setColumnFilterCriteria(9, filterCriteriafecha)
.getRange();//range.getFilter().remove();
}
But when i use GetValues take all values, filter and not filter
If you want to retrieve the data and manipulate it in Google Apps Script, you could create temporary sheet, copy filtered data to temporary sheet using method:copyTo() with copyPasteType PASTE_NORMAL and use method:getDataRange() & method:getValues() to retrieve the data.
Example Data:
I copied TheMaster answer here and added some features:
function getFilteredValues(){
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = activeSpreadsheet.getSheetByName("Temporary");
//check if existing, delete if yes
if (newSheet != null) {
activeSpreadsheet.deleteSheet(newSheet);
}
//create new sheet with name Temporary
newSheet = activeSpreadsheet.insertSheet();
newSheet.setName("Temporary");
var dataSheet = activeSpreadsheet.getSheetByName("Sheet1");
var toFilter = dataSheet.getDataRange();
var filter = toFilter.createFilter();
//create criteria
var criteria = SpreadsheetApp.newFilterCriteria();
criteria.whenNumberGreaterThan(1200);
//filter first column using the criteria above
filter.setColumnFilterCriteria(1, criteria.build());
//copy filtered data to temporary sheet
var sourceRange = dataSheet.getFilter().getRange();
sourceRange.copyTo(
newSheet.getRange('A1'),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL,
false);
Logger.log(newSheet.getDataRange().getValues());
activeSpreadsheet.deleteSheet(newSheet);
}
Output:
Note: You can also use this to paste the data to sheet.
This will do it. You just pass the range to the removeFilteredData() function and it will return the filtered array.
/**
* #param {SpreadsheetApp.Spreadsheet.Range} range
* #returns {Array<Array>}
*/
function removeFilteredValues(range) {
const values = range.getValues();
const firstRow = range.getRow();
const sheet = range.getSheet();
const filteredValues = values.filter((row, i) => {
return !(sheet.isRowHiddenByFilter(i + firstRow));
});
return filteredValues;
}
function main() {
const range = SpreadsheetApp
.getActiveSpreadsheet()
.getActiveSheet()
.getDataRange();
const result = removeFilteredValues(range);
}
The following is what I use from #Nikko idea above. You can copy the result to different spreadsheet.
function copyFilteredData(sourceID, sheetName, desID, desSheetName, filterCol, filterValue){
const sourceSS = SpreadsheetApp.openById(sourceID);
const sheet = sourceSS.getSheetByName(sheetName);
// remove filter if any
if (sheet.getFilter()) {
sheet.getFilter().remove();
}
var toFilter = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var filter = toFilter.createFilter();
// create criteria
var criteria = SpreadsheetApp.newFilterCriteria();
criteria.whenTextEqualTo(filterValue);
// filter first column using the criteria above
filter.setColumnFilterCriteria(filterCol, criteria.build());
// copy filtered data
const destinationSS = SpreadsheetApp.openById(desID);
const destSheet = destinationSS.getSheetByName(desSheetName);
var sourceRange = sheet.getFilter().getRange();
sourceRange.copyTo(
destSheet.getRange('A1'),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL,
false);
// clear our filter before leaving
if (sheet.getFilter()) {
sheet.getFilter().remove();
}
}
I'm creating a spreadsheet to track who did what on a day to day basis. For complicated reasons I don't want to go into the sheet has to be sorted by the rows rather than the columns.
My full script works great except it clears out the background colors of each cell when it transposes. I could transpose and sort by hand and keep the cell backgrounds (Certain cells have to be color-coded based on input from another sheet) but that's tedious and it's why people script in the first place.
I've tried editing the code below to getBackgrounds() and setBackgrounds() in various ways. I've come to the conclusion that I need help.
function Transpose() {
//This function Transposes it in order to sort since google doesn't let you sort by rows.
// get all the data in the sheet
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet3');
var range = ss.getDataRange();
var values = range.getValues();
// clear existing
range.clear();
// transpose it & write it out
ss.getRange(1,1,values[0].length,values.length)
.setValues(Object.keys(values[0]).map ( function (columnNumber) {
return values.map( function (row) {
return row[columnNumber];
});
}));
}
Modifications
Access backgrounds somewhere along the lines:
var backgrounds = range.getBackgrounds();
Move transpose logic to a utility function and optimize it:
/**
* Transposes values;
* #param {Array<Array>} values input Array;
* #return {Array<Array>} transposed Array;
*/
function transpose(values) {
return values[0].map(function(col,c){
return values.map(function(row){
return row[c];
});
});
}
Combine previous steps in your main function, clean and simple:
function Transpose() {
// get all the data in the sheet
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet3');
var range = ss.getDataRange();
//access values and backgrounds;
var values = range.getValues();
var backgrounds = range.getBackgrounds();
// clear existing;
range.clear();
//transpose;
values = transpose(values);
backgrounds = transpose(backgrounds);
//access target range - note that dimensions are inversed;
var target = ss.getRange(1,1,values.length,values[0].length);
//set values and backgrounds;
target.setValues(values);
target.setBackgrounds(backgrounds);
}
You could just use the inbuilt transpose:
function transpose() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet3');
var range = ss.getDataRange();
range.copyTo(
ss.getRange('A1'),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL,
true
);
}
If you want to ignore formulas and just copy values/format,
["VALUES","FORMAT"].forEach(function(type){
range.copyTo(
ss.getRange('A1'),
SpreadsheetApp.CopyPasteType["PASTE_" + type ],
true
);
})
References:
Range#copyTo
Just apply backgrounds too
forEach as a consistent solution
/**
* Transposes with backgrounds
*/
function transpose() {
// get all the data in the sheet
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet3');
var range = ss.getDataRange();
var values = range.getValues();
var backgrounds = range.getBackgrounds();
var transposeBackgrounds = [];
var transposeValues = [];
values[0].forEach(function(_, i) {
var rowValues = [];
var rowBackgrounds = [];
values.forEach(function(_, j) {
rowValues.push(values[j][i]);
rowBackgrounds.push(backgrounds[j][i]);
});
transposeValues.push(rowValues);
transposeBackgrounds.push(rowBackgrounds);
});
// clear existing
range.clear();
// transpose it & write it out
ss.getRange(1, 1, transposeValues.length, transposeValues[0].length)
.setValues(transposeValues)
.setBackgrounds(transposeBackgrounds);
}
The implementation of the current code
Based of that you can implement it on your code
function Transpose() {
// This function Transposes it in order to sort since bla-bla-bla
// get all the data in the sheet
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet3');
var range = ss.getDataRange();
var values = range.getValues();
var backgrounds = range.getBackgrounds();
var transposeBackgrounds = [];
// clear existing
range.clear();
// transpose it & write it out
ss.getRange(1, 1, values[0].length, values.length)
.setValues(
Object.keys(values[0]).map(function(columnNumber) {
var rowBackgrounds = [];
var newRow = values.map(function(row, i) {
rowBackgrounds.push(backgrounds[i][columnNumber]);
return row[columnNumber];
});
transposeBackgrounds.push(rowBackgrounds);
return newRow;
})
)
.setBackgrounds(transposeBackgrounds);
}
I'm attempting to switch from Excel to Google Sheets and I already have a macro for this, but you can't run macros on Google spreadsheet. What I want is to move the Value in Log-in!C5 to another sheet "w1" starting in column A3. The code I have works but instead of moving it in "w1!A3" it was placed on cell A146 since it was the next empty row. Is there a way for the value to be moved in first empty row in Column A? regardless that the other columns are not empty?
Here is my code
function moveValuesOnly() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange('Log-in!C5');
var destSheet = ss.getSheetByName('w1');
var destRange = destSheet.getRange(destSheet.getLastRow()+1,1);
source.copyTo (destRange, {contentsOnly: true});
source.clear ();
}
getLastRow() only gets the lastRow of the sheet and not the range.
If you only want w1!A3,hardcode it directly: Try Changing from
destSheet.getRange(destSheet.getLastRow()+1,1);
to
destSheet.getRange(3,1);
Alternatively,To find the first empty cell in a range,
var colA = destSheet.getRange('A:A').getValues();
Code1:
//Filter 2D array to 1D
var fcolA = colA.filter(function (e) {return e[0];});
var firstEmptyRowIncolA = fcolA.indexOf('')+1;
or
Code2:
for(i=0;i<colA.length;i++){
if(colA[i][0] == ''){
var firstEmptyRowIncolA = i+1;
break;
}
}
I created a button that does the following:
A. Copy a group of columns from sheet 1 (daily sports) and paste them into sheet 2 (Record).
B. The columns pasted in sheet 2 go to the next empty column (or create a new column) so they don't over-right the info that's already there..
C. Every time I'm done putting in new information I press the button and it automatically gets copied to sheet 2.
This is the code I'm using:
function moveValuesOnly () {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var source = ss.getRange ("Daily sports!B2:D20");
var destSheet = ss.getSheetByName("Record");
var destRange = destSheet.getRange(destSheet.getLastRow(),1);
var destRange = destSheet.getRange(destSheet.getLastColumn(),2);
source.copyTo (destRange, {contentsOnly: true});
source.clear ();}
The problems I'm having are as follows
1) Every time I hit the button it does not create a new column, instead it just covers the old columns.
2) the original source gets cleared (I know, It's the last function, it clearly says "clear" I just don't know what to write instead... I want the source to stay as is)
If anyone could help me with this script that would be great..
Thank you!
The following code should do what are you looking for:
function moveValuesOnly() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange ("Daily sports!B2:D20");
var destSheet = ss.getSheetByName("Record");
var row = 1; //the starting row of the range
var column = destSheet.getLastColumn()+1; //the starting column of the range
var numRows = 19; //the number of rows to return
var numColumns = 3; //the number of columns to return
var destRange = destSheet.getRange(row, column, numRows, numColumns);
source.copyTo(destRange, {contentsOnly: true})
}