Is it possible to fasten the hide of many rows when range.length > 300 rows ?
I also can't succeed moving the focus to the top of the sheet once the rows are hidden, I can only get the focus on another sheet.
Here is my code (french parameters), I'm not sure I need to show my spreadsheet. Thank you very much.
var LastRow = sheet.getLastRow()-1;
var ToHide = [];
for (var i=1 ; i < LastRow +1 ; i++){
if ( sheet.getRange(i,1).isChecked() == null){ ToHide.push(i); }
}
for (var j=0 ; j<ToHide.length ; j++){ MyActiveSheet.hideRows(ToHide[j+1],1); }
ToHide.forEach(function (d){ FeuilleActive.hideRows(d); }); // also tried .hideRows(d,1)
SpreadsheetApp.getActive().getSheets()[5].getRange(1,1).activate(); //
To lessen the number of loops, you can group it by determining the series of consecutive numbers in your Array. Which you can use to determine the index and number of rows to hide. The number of .hideRows() execution will be determined by the number of series in your array. Thus, lesser runtime.
Example Code:
function myFunction() {
var a = [1,2,3,4,6,7,8,9,11,13,14];
const result = a.reduce((r, n) => {
const lastSubArray = r[r.length - 1];
if(!lastSubArray || lastSubArray[lastSubArray.length - 1] !== n - 1) {
r.push([]);
}
r[r.length - 1].push(n);
return r;
}, []);
//result output: [[1.0, 2.0, 3.0, 4.0], [6.0, 7.0, 8.0, 9.0], [11.0], [13.0, 14.0]]
result.forEach(e => {
var index = e[0];
var numRows = e.length;
Logger.log("Index: "+ index);
Logger.log("numRows: "+numRows);
// MyActiveSheet.hideRows(index,numRows);
})
}
Output:
References:
Ori Drori answer on how to group series of consecutive numbers in an Array.
hideRows(rowIndex, numRows)
Related
Sorry in advanced for bad English. I have 15+ rows and 15+ columns. I need to merge cells in a spreadsheet but data will be unclear after merging. Is there a formula/function I can use to clarify my merge? Thank you in advance.
Please view example spreadsheet.
This is not possible using only formula. Formulas can't do anything to formatting like merging cells and applying colors to text. But here's a script you can use to begin with. You may modify it to apply font color.
Try:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("Sheet1");
var destSheet = ss.getSheetByName("Sheet2");
var sourceData = sourceSheet.getRange(2, 1, sourceSheet.getLastRow() - 1, sourceSheet.getLastColumn()).getValues();
//Add name to each column, add here if you have more columns
sourceData.forEach(function (x) {
x[2] = x[1] + ":\n" + x[2]
x[3] = x[1] + ":\n" + x[3]
});
var array = sourceData,
hash = {},
i, j,
result,
item,
key;
for (i = 0; i < array.length; i++) {
item = array[i];
key = item[0].toString();
if (!hash[key]) {
hash[key] = item.slice();
continue;
}
for (j = 1; j < item.length; j++) hash[key][j] = hash[key][j] + "\n" + item[j];
}
result = Object.values(hash);
destSheet.getRange(2, 1, result.length, result[0].length).setValues(result);
}
Result:
Reference:
How to merge an array of arrays with duplicate at index 0 into one array javascript
Hope this helps!
You can try with this formula, adapt the column range in the MAP function if necessary:
={UNIQUE('raw data'!A2:A),
SCAN(,UNIQUE('raw data'!A2:A),
LAMBDA(x,city, BYCOL(IFNA(FILTER ({'raw data'!B2:B,
MAP('raw data'!C2:G,LAMBDA(a,IF(a="","",INDEX('raw data'!B:B,ROW(a))&":"&CHAR(10)&a)))},'raw data'!A2:A=city,'raw data'!A2:A<>"")),LAMBDA(col,JOIN(CHAR(10)&CHAR(10),col)))))}
I'm looking to clone a row 3x, but only keeping data from one column.
So essentially I have the following [Name / Time / Booking], and each row is populated with all 3 properties, I'm trying to create 3 blank rows underneath each current row which is populated with only the persons name.
Can't work how to do it in scripting and can't find a plugin to do this. My data set is over 10,000 big so doing it manually isn't an option.
What I have:
What I want:
UPDATED code:
function duplicateRows() {
var sh, v, arr, c, b;
sh = SpreadsheetApp.getActive()
.getSheetByName('Blad1')
v = sh.getRange(1, 1, sh.getLastRow(), 40)
.getValues();
arr = [v[0]];
v.splice(1)
.forEach(function (r, i) {
arr.push(r)
c = 0
while (c < 3) {
dup = makeEmptyArrayXEl(40)
dup[0] = r[0];
arr.push(dup)
c += 1;
}
})
sh.getRange(1, 1, arr.length, arr[0].length)
.setValues(arr);
}
function makeEmptyArrayXEl(num) {
var arr = [];
for (var i = 0; i < num; i++) {
arr.push("")
}
return arr;
}
Would this work for you? It requires a free column to the left of Booking in the original data set. The formula below is a new sheet.
=ArrayFormula(sort({A2:A4,B2:B4,C2:C4;A2:A4,D2:D4,D2:D4;A2:A4,D2:D4,D2:D4;A2:A4,D2:D4,D2:D4},1,FALSE))
I have 30 columns and 1000 rows, I would like to compare column1 with another column. IF the value dont match then I would like to colour it red. Below is a small dataset in my spreadsheet:
A B C D E F ...
1 name sName email
2
3
.
n
Because I have a large dataset and I want to storing my columns in a array, the first row is heading. This is what I have done, however when testing I get empty result, can someone correct me what I am doing wrong?
var index = [];
var sheet = SpreadsheetApp.getActiveSheet();
function col(){
var data = sheet.getDataRange().getValues();
for (var i = 1; i <= data.length; i++) {
te = index[i] = data[1];
Logger.log(columnIndex[i])
if (data[3] != data[7]){
// column_id.setFontColor('red'); <--- I can set the background like this
}
}
}
From the code you can see I am scanning whole spreadsheet data[1] get the heading and in if loop (data[3] != data[7]) compare two columns. I do have to work on my colour variable but that can be done once I get the data that I need.
Try to check this tutorial if it can help you with your problem. This tutorial use a Google AppsScript to compare the two columns. If differences are found, the script should point these out. If no differences are found at all, the script should put out the text "[id]". Just customize this code for your own function.
Here is the code used to achieve this kind of comparison
function stringComparison(s1, s2) {
// lets test both variables are the same object type if not throw an error
if (Object.prototype.toString.call(s1) !== Object.prototype.toString.call(s2)){
throw("Both values need to be an array of cells or individual cells")
}
// if we are looking at two arrays of cells make sure the sizes match and only one column wide
if( Object.prototype.toString.call(s1) === '[object Array]' ) {
if (s1.length != s2.length || s1[0].length > 1 || s2[0].length > 1){
throw("Arrays of cells need to be same size and 1 column wide");
}
// since we are working with an array intialise the return
var out = [];
for (r in s1){ // loop over the rows and find differences using diff sub function
out.push([diff(s1[r][0], s2[r][0])]);
}
return out; // return response
} else { // we are working with two cells so return diff
return diff(s1, s2)
}
}
function diff (s1, s2){
var out = "[ ";
var notid = false;
// loop to match each character
for (var n = 0; n < s1.length; n++){
if (s1.charAt(n) == s2.charAt(n)){
out += "–";
} else {
out += s2.charAt(n);
notid = true;
}
out += " ";
}
out += " ]"
return (notid) ? out : "[ id. ]"; // if notid(entical) return output or [id.]
}
For more information, just check the tutorial link above and this SO question on how to compare two Spreadsheets.
im rearranging a spreadsheet using google script. I am first sorting it and then moving some rows within a range to the end of the sheet. Everytime i do the moveTo function some cells that reference the moved rows get changed to reflect the new row numbers even though the cells are outside my range and should not be modified. For example if im moving cell b3 and i have cell f4 =b3 then when i move b3 cell f4 changes to be whatever b3 is now. i tried locking it with =b$3 but still didnt work. Also it messes up the conditional formatting that should be in place for the entire column using something like "d2:e" and it changes to be something like "d2:e109" or something similar. Any clue whats going on?
function onEdit(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var allowedSheet = 1;
if(sheet.getIndex() == allowedSheet) {
var editedCell = sheet.getActiveCell();
var sortBy = [1, 3, 2];
var triggerCol = [1,2,3,10,11,12];
var rangeStart = "A";
var rangeEnd = "E";
var tableRange = "A2:E";
if(triggerCol.indexOf(editedCell.getColumn()) > -1) {
var range = sheet.getRange(tableRange);
range.sort([{column: sortBy[0], ascending: true}, {column: sortBy[1], ascending: false}, {column: sortBy[2], ascending: true}]);
var indexOfIncome = find( sheet, 2, "Income");
if( indexOfIncome > 0 ){
var overflowRange = sheet.getRange("A2:E" + (indexOfIncome - 1 ));
var lastRow = findFirstEmptyCell( sheet, 1 );
overflowRange.moveTo(sheet.getRange("A" + ( lastRow )));
var fullRange = sheet.getRange(rangeStart + indexOfIncome + ":" + rangeEnd);
fullRange.moveTo(sheet.getRange(rangeStart + "2"));
}
}
}
}
function find( sheet, column, value ) {
var data = sheet.getRange(1, column, sheet.getMaxRows()).getValues();
for( i = 0; i < sheet.getMaxRows(); i++ ){
if (data[i][0].toString().indexOf(value) > -1 ) {
return i + 1;
}
}
return 0;
}
function findFirstEmptyCell ( sheet, column ){
var data = sheet.getRange( 1, column, sheet.getMaxRows() ).getValues();
for( i = 0; i < sheet.getMaxRows() ; i++ ){
if( data[i][0] == "" ){
return i + 1;
}
}
return 0;
}
It's an expected behaviour, moveTo works just like Cut (Ctrl+X), while your looking for Copy+Paste then delete original content, which should be copyTo(newRange) associated with clear(oldRange).
I got it to work by using copy and then clearing. Now why in the world google would make a single function that behaves the exact opposite from all the other functions is beyond me. The documentation says moveto behaves as cut and paste but when i cut and paste i do not mess up the rest of my sheet.
How can I compute a MD5 or SHA1 hash of text in a specific cell and set it to another cell in Google Spreadsheet?
Is there a formula like =ComputeMD5(A1) or =ComputeSHA1(A1)?
Or is it possible to write custom formula for this? How?
Open Tools > Script Editor then paste the following code:
function MD5 (input) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);
var txtHash = '';
for (i = 0; i < rawHash.length; i++) {
var hashVal = rawHash[i];
if (hashVal < 0) {
hashVal += 256;
}
if (hashVal.toString(16).length == 1) {
txtHash += '0';
}
txtHash += hashVal.toString(16);
}
return txtHash;
}
Save the script after that and then use the MD5() function in your spreadsheet while referencing a cell.
This script is based on Utilities.computeDigest() function.
Thanks to gabhubert for the code.
This is the SHA1 version of that code (very simple change)
function GetSHA1(input) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1, input);
var txtHash = '';
for (j = 0; j <rawHash.length; j++) {
var hashVal = rawHash[j];
if (hashVal < 0)
hashVal += 256;
if (hashVal.toString(16).length == 1)
txtHash += "0";
txtHash += hashVal.toString(16);
}
return txtHash;
}
Ok, got it,
Need to create custom function as explained in
http://code.google.com/googleapps/appsscript/articles/custom_function.html
And then use the apis as explained in
http://code.google.com/googleapps/appsscript/service_utilities.html
I need to handtype the complete function name so that I can see the result in the cell.
Following is the sample of the code that gave base 64 encoded hash of the text
function getBase64EncodedMD5(text)
{
return Utilities.base64Encode( Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, text));
}
The difference between this solution and the others is:
It fixes an issue some of the above solution have with offsetting the output of Utilities.computeDigest (it offsets by 128 instead of 256)
It fixes an issue that causes some other solutions to produce the same hash for different inputs by calling JSON.stringify() on input before passing it to Utilities.computeDigest()
function MD5(input) {
var result = "";
var byteArray = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, JSON.stringify(input));
for (i=0; i < byteArray.length; i++) {
result += (byteArray[i] + 128).toString(16) + "-";
}
result = result.substring(result, result.length - 1); // remove trailing dash
return result;
}
to get hashes for a range of cells, add this next to gabhubert's function:
function RangeGetMD5Hash(input) {
if (input.map) { // Test whether input is an array.
return input.map(GetMD5Hash); // Recurse over array if so.
} else {
return GetMD5Hash(input)
}
}
and use it in cell this way:
=RangeGetMD5Hash(A5:X25)
It returns range of same dimensions as source one, values will spread down and right from cell with formulae.
It's universal single-value-function to range-func conversion method (ref), and it's way faster than separate formuleas for each cell; in this form, it also works for single cell, so maybe it's worth to rewrite source function this way.
Based on #gabhubert but using array operations to get the hexadecimal representation
function sha(str){
return Utilities
.computeDigest(Utilities.DigestAlgorithm.SHA_1, str) // string to digested array of integers
.map(function(val) {return val<0? val+256 : val}) // correct the offset
.map(function(val) {return ("00" + val.toString(16)).slice(-2)}) // add padding and enconde
.join(''); // join in a single string
}
Using #gabhubert answer, you could do this, if you want to get the results from a whole row. From the script editor.
function GetMD5Hash(value) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, value);
var txtHash = '';
for (j = 0; j <rawHash.length; j++) {
var hashVal = rawHash[j];
if (hashVal < 0)
hashVal += 256;
if (hashVal.toString(16).length == 1)
txtHash += "0";
txtHash += hashVal.toString(16);
}
return txtHash;
}
function straightToText() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var r = 1;
var n_rows = 9999;
var n_cols = 1;
var column = 1;
var sheet = ss[0].getRange(r, column, n_rows, ncols).getValues(); // get first sheet, a1:a9999
var results = [];
for (var i = 0; i < sheet.length; i++) {
var hashmd5= GetMD5Hash(sheet[i][0]);
results.push(hashmd5);
}
var dest_col = 3;
for (var j = 0; j < results.length; j++) {
var row = j+1;
ss[0].getRange(row, dest_col).setValue(results[j]); // write output to c1:c9999 as text
}
}
And then, from the Run menu, just run the function straightToText() so you can get your result, and elude the too many calls to a function error.
I was looking for an option that would provide a shorter result. What do you think about this? It only returns 4 characters. The unfortunate part is that it uses i's and o's which can be confused for L's and 0's respectively; with the right font and in caps it wouldn't matter much.
function getShortMD5Hash(input) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);
var txtHash = '';
for (j = 0; j < 16; j += 8) {
hashVal = (rawHash[j] + rawHash[j+1] + rawHash[j+2] + rawHash[j+3]) ^ (rawHash[j+4] + rawHash[j+5] + rawHash[j+6] + rawHash[j+7])
if (hashVal < 0)
hashVal += 1024;
if (hashVal.toString(36).length == 1)
txtHash += "0";
txtHash += hashVal.toString(36);
}
return txtHash.toUpperCase();
}
I needed to get a hash across a range of cells, so I run it like this:
function RangeSHA256(input)
{
return Array.isArray(input) ?
input.map(row => row.map(cell => SHA256(cell))) :
SHA256(input);
}