Add columns to an existing source of data - arrays

I would like to add several columns to an existing source of data displayed in a sheet. But as a data source, the number of rows can increase. I don't want to update the sheet once it is set up, it should be automatic. In those new columns, I would like formulas.
Please find below the result expected.
in blue, columns from data source
in red, my new columns with a formula
How can I do ?
Thank for you help 🙏

use in E1:
={"new col 1"; ARRAYFORMULA(IF(C2:C="";;IF((ISNUMBER(C2:C))*(C2:C>=2); 1; 0)))}
use in F1:
={"new col 2"; ARRAYFORMULA(IF(D2:D="";;IF((ISNUMBER(D2:D))*(D2:D>=2); 10; 0)))}

I tried with a custom function and to personalize it with another project, it doesn't work...
In this simple case, it works. If not empty, it returns G cells in uppercase :
={"Feature"; ARRAYFORMULA(SI(G2:G="";;IF((NOT(ISEMPTY(G2:G))); UPPERCASE(G2:G); "-")))}
In other case with a custom function, not. It is named "getFeature"
={"Feature"; ARRAYFORMULA(SI(G2:G="";;IF((NOT(ISEMPTY(G2:G))); getFeature(G2:G); "-")))}
When I use it in a classic way, my function works : =getFeature(G7)
Here my custom function :
function getFeature(searchString) {
if (searchString === "") {
return ""
}
var sFeature = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("raw feature")
var rFeatureCol = sFeature.getRange(2, 1, sFeature.getLastRow(), 1)
array = searchString.split(";")
for (let i = 0; i < array.length; i++) {
try {
var textFinder = rFeatureCol.createTextFinder(array[i])
var search_row = textFinder.findNext().getValue()
return array[i]
}
catch {
// erreur détectée
}
}
return ""
}

Related

Apps Script: Move SELECTED rows from one sheet to the last available rows of another sheet

The code below is from here and was originally written by user #Cooper. All it does is that it copies the values of the columns specified in the array colA = [1,3,2,4] from SHEET 1 and add them to SHEET 2. I want to re-write the 2 last lines of the code, so that the values coming from SHEET 1 are added to the last available rows in SHEET 2. By "2 last lines", I mean these last lines of the code:
drng = des.getRange(1,1,drngA.length,drngA[0].length);//destination array controls size of destination range.drng.setValues(drngA);
Any idea how to get this done?
Thank you so much in advance for your help!
function Copy_data()
{
var src = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SHEET 1");
var des = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SHEET 2");
var srng = src.getDataRange();
var srngA = srng.getValues();
var drngA = [];
var colA = [1,3,2,4];//indirect data column selection
for (var i = 0; i < srngA.length;i++ )
{
var k = 0;
drngA[i]=[];
for(var j=0;j<srngA[i].length;j++)
{
drngA[i][k++] = srngA[i][colA[j]-1];
}
drng = des.getRange(1,1,drngA.length,drngA[0].length);//destination array controls size of destination range.
drng.setValues(drngA);
}
}
You can get the last row of the destination Sheet and after, add it to your getRange() function. The method proposed by #soMario will not work because it makes requests inside the loop while you update the Sheet, and this causes it to get different values every time you call des.getLastRow().
The simplest solution is to take the getLastRow() request outside of the loop and then include it in getRange. Note that one is added to it so that it does not overwrite the last row with the setValue() request.
Code.gs
function Copy_data() {
...
var lastRow = des.getLastRow() + 1
for (var i = 0; i < srngA.length;i++ ){
...
drng = des.getRange(lastRow,1,drngA.length,drngA[0].length)
...
}
Documentation
getRange(row, column, numRows, numColumns)
getLastRow()

set value on sidebar

I am having one google sheet having more than 100 rows with column of "NAME, PLACE, PHONE". I want to change /correct the phone number on specific person Ex.John in the side bar (Form.html) and the correct place & phone number to be edit in that specific row of my google sheet "Phonelist". The code.gs given below which is not working. Could you lease rectify the same?
function sidebar() {
var html = HtmlService.createHtmlOutputFromFile("Form").setTitle('Phone Details');
SpreadsheetApp.getUi().sidebar(html);
}
function result(form) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ws = ss.getSheetByName("Phonelist");
var data = ws.getDataRange().getValues();
var name = form.name;
var place = form.place;
var phone = form.phone;
for (var i = 1; i < data.length; i++) {
if(data[i][1] == "John"){
var result = [name,place,phone];
ws.getRange(dat[i]).setValue(result);
}
}
}
It is difficult to understand what you exactly need. But there are some issues which are visible.
ws.getRange(data[i])is not valid. See docs. You need a row and a column at least, and in your case also the number of columns since your are inserting a range. Currently you only have a column. The solution is `
const startColumn = 1 // start at column A
const numberOfRows = 1 // update one row at a time
const numberOfColumns = result.length // this will be 3
ws.getRange(data[i], startColumn, numberOfRows, result.length)
.setValues(result) // setValues is correct, setValue is incorrect
The second issue is that you said that NAME is in the first column, but your test is testing against the second column. Array start at 0, i.e. the first item is actual accessed by [0]. therefore your test if(data[i][1] == "John") actually checks if the second column PLACE is equal to "John". To fix this, replace the [1] with [0], so:
if(data[i][0] == "John")
The third issue is handled in the first answer. You are using setValue() which is only to be used to set one cell. But since you are setting a number of cells at one time, you should use setValues() instead.

Is there a way to highlight duplicate combinations of values in two columns, regardless of the order?

I have a UFC database and I'm looking for rematches. Therefore, I need to find the duplicate combinations of names; the combinations that appear more than once. However, since the winner of the first fight could lose the rematch, I need to find duplicates regardless of the order in which they appear.
This is how my database is structured:
database example.
Fighter 1 is the winner and fighter 2 is the loser.
Here is a link to the database (got it from kaggle): https://docs.google.com/spreadsheets/d/19ISNhYFdGzgLZz1x4h2v_Q5Pq0cofw2rkUtwk3xPXGQ/edit?usp=sharing
Feel free to play around with it.
Any ideas on how to solve this?
Here is an example of the result I'd ideally want:
image example
Just a simple highlight over the duplicates.
In order to give you more precise help, it would be helpful if you could give an example of the result you want. As has been said, if there are many duplicates and you mark each group with a different colour, it can be visually confusing.
Anyway, I have written a couple of functions with Apps Script and Spreadsheet Service that you may find useful for your project.
Code 1
With these functions, you will be able to get all fights where two fighters that you previously define have participated:
function main() {
var name1 = 'Conor McGregor'
var name2 = 'Nate Diaz'
var result = findDuplicates(name1, name2)
console.log(result)
}
function findDuplicates(name1, name2) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark1')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var result = []
for (var i = 0; i < range.length; i++) {
if (range[i].includes(name1) && range[i].includes(name2)) {
result.push(range[i])
}
}
return result
}
Code 2
With this function, you can add in a third column, the same number for each pair of fighters. This way, it is very easy to change the "add a number" to "paint the cells in a colour".
function findPairs() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark2')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var indexToSkip = []
var k = 0
for (var i = 0; i < range.length; i++) {
if (!indexToSkip.includes(i)) {
var match = range[i]
indexToSkip.push(i)
sheet.getRange(i + 1, 3).setValue(k)
for (var j = i + 1; j < range.length; j++) {
if (range[j].includes(match[0]) && range[j].includes(match[1])) {
indexToSkip.push(j)
sheet.getRange(j + 1, 3).setValue(k)
}
}
k = k+1
}
}
}
References:
Apps Script
Spreadsheet Service

How to make a data array with values and formulas

I'm trying to alter some code from a tutorial on how to remove duplicate rows from my sheet. The code works great for straight values, but some of my rows have cells with hyperlink formulas that I don't want to lose.
I think I need to make the spreadsheet data as an array, rather than just using getValues. It's been a long time since I've had to make an array, so I'm struggling with the best way to do this. I'll need for loops to populate the array and if statements to either get values or formulas depending on the row.
Here's the remove duplicates code that removes rows if the cell in column A and B match another row:
/**
* Removes duplicate rows from the current sheet.
*/
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var newData = [];
for (var i in data) {
var row = data[i];
var duplicate = false;
for (var j in newData) {
if(row[0] == newData[j][0] && row[1] == newData[j][1]){
duplicate = true;
}
}
if (!duplicate) {
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
So instead of just using one line to set the data variable, I need to build the array with values and formulas where appropriate. Otherwise, I lose my hyperlinks. How do I do this?
Edit: This question was solved by using an answer to another question. The questions' contexts are not the same, and the other question did not show up in any of my research, so I believe my question is not a duplicate.
Thanks to the comment by TheMaster, I was able to solve this. Here's the original "Remove Duplicates" tutorial, and here's the question TheMaster directed me to to find the solution.
My final code:
/**
* Removes duplicate rows from the current sheet.
*/
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var newData = [];
var data = range.getValues();
data = range.getFormulas().map(function(e, i) {//i=index of row(e)
return e.map(function(f, j) {//j = index of column(f)
return f === "" ? data[i][j] : f;
});
});
for (var i in data) {
var row = data[i];
var duplicate = false;
for (var j in newData) {
if(row[0] == newData[j][0] && row[1] == newData[j][1]){
duplicate = true;
}
}
if (!duplicate) {
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}

Improve function delete rows by contain value

I have working code, that delete row from sheet if column corresponds to one of the conditions. It based on arrays and because of it works much more faster than standard google sheet deleteRow function. I call it like this:
deleteRowsBV('SIZ',4,'0','')
where
deleteRowsBV(listName,ColNum,FirstSearch,SecondSearch)
What I want is a call function with more or less and equal signs, like this:
deleteRowsBV('SIZ',4,<='0',=='')
But in case of my main function, it doesn't work, when I specify a variable instead of a sign and a value.
Here is main function:
function deleteRowsBV(listName,ColNum,FirstSearch,SecondSearch) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(listName);
var DataLengBefore = sheet.getLastRow();
var DataColLeng = sheet.getLastColumn();
var data = sheet.getRange(1,1,DataLengBefore,DataColLeng).getValues();
for (var i = data.length-1; i >= 0; i--) {
if (data[i][ColNum] <= FirstSearch||data[i][ColNum] == SecondSearch) {
data.splice(i, 1);
}
}
sheet.getRange(10, 1, DataLengBefore,DataColLeng).clearContent();
sheet.getRange(10, 1,data.length,5).setValues(data);
}
Based from this related post, spreadsheet rows and columns are numbered starting at 1, for all methods in the SpreadsheetApp, while javascript arrays start numbering from 0. You need to adjust between those numeric bases when working with both. When deleting rows, the size of the spreadsheet dataRange will get smaller; you did take that into account, but because you're looping up to the maximum size, the code is complicated by this requirement. You can simplify things by looping down from the maximum.
You may refer with this thread. However, this only looks at the value from a single cell edit now and not the values in the whole sheet.
function onEdit(e) {
//Logger.log(JSON.stringify(e));
//{"source":{},"range":{"rowStart":1,"rowEnd":1,"columnEnd":1,"columnStart":1},"value":"1","user":{"email":"","nickname":""},"authMode":{}}
try {
var ss = e.source; // Just pull the spreadsheet object from the one already being passed to onEdit
var s = ss.getActiveSheet();
// Conditions are by sheet and a single cell in a certain column
if (s.getName() == 'Sheet1' && // change to your own
e.range.columnStart == 3 && e.range.columnEnd == 3 && // only look at edits happening in col C which is 3
e.range.rowStart == e.range.rowEnd ) { // only look at single row edits which will equal a single cell
checkCellValue(e);
}
} catch (error) { Logger.log(error); }
};
function checkCellValue(e) {
if ( !e.value || e.value == 0) { // Delete if value is zero or empty
e.source.getActiveSheet().deleteRow(e.range.rowStart);
}
}

Resources