Score calculation, considering negative marking - google-apps-script

I'm trying to find if I can ask a script to calculate a grade where for each good answer it gives +1, and for each wrong answer it substracts -0,25. The calculation will be made in column C, as shown in the screenshot. In the example, the correct score is 6,25 instead of 7.
BTW, the score calculation refers to the key answers on the row 2.

I believe your goal as follows.
You want to calculate for the values of column "C" by checking the background colors of each cell using Google Apps Script.
When the background color of cell is #3ace9c (green), you want to add 1.
When the background color of cell is #ff9900 (orange), you want to reduce 0.25.
You want to calculate for the values of column "C" by comparing the columns "D" to "M" at the row 2 (the referring answer) and each row (after row 3) using Google Apps Script.
When the columns "D" to "M" at the row 2 are the same with each row, you want to add 1.
When the columns "D" to "M" at the row 2 are NOT the same with each row, you want to reduce 0.25.
For this, how about this answer?
Pattern 1:
In this pattern, the background colors are used. The flow of this sample script is as follows.
Prepare an object including the color codes for "good" and "wrong" answers.
Retrieve the background colors from the range of "D3:M" in the sheet.
Calculate the values using the object and the background colors.
Put the values to the column "C" in the sheet.
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
// 1. Prepare an object including the color codes for "good" and "wrong" answers.
const obj = {"#3ace9c": 1, "#ff9900": -0.25}; // Please set the color codes, if you change the color.
// 2. Retrieve the background colors from the range of "D3:M" in the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const backgrounds = sheet.getRange("D3:M" + sheet.getLastRow()).getBackgrounds();
// 3. Calculate the values using the object and the background colors.
const result = backgrounds.map(r => [r.reduce((c, e) => c += obj[e], 0)]);
// 4. Put the values to the column "C" in the sheet.
sheet.getRange(3, 3, result.length, 1).setValues(result);
}
Pattern 2:
In this pattern, the referring answer of the row 2 is used. The flow of this sample script is as follows.
Retrieve values from the range of "A2:M" in the sheet.
Retrieve the referring answer.
Calculate the values of each values by comparing the referring answer, and create an array including the result values.
Put the values to the column "C" in the sheet.
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheet name.
// 1. Retrieve values from the range of "A2:M" in the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const values = sheet.getRange("A2:M" + sheet.getLastRow()).getValues();
// 2. Retrieve the referring answer.
const answers = values.shift();
answers.splice(0, 3);
// 3. Calculate the values of each values by comparing the referring answer, and create an array including the result values.
const result = values.map(([,,c,...dm]) => [dm.reduce((c, e, i) => e == answers[i] ? c + 1 : c - 0.25, 0)]);
// 4. Put the values to the column "C" in the sheet.
sheet.getRange(3, 3, result.length, 1).setValues(result);
}
References:
map()
reduce()
getBackgrounds()
getValues()
setValues()

Related

Google Sheet Macro - how do I return a column number based on dynamic cell contents?

I have the following code as a starting point. I want to select the entire column where row 3 contains a specific date value (it will be the date of the previous Monday; I have a formula returning this date in cell E1).
function selectDate() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
ss.getRange(3,?,1,ss.getMaxColumns()).activate();
}
Basically, the getRange column value would be interpreted as something like: "Find the column number where the value in row 3 is equal to the value in cell E1".
Any ideas would be very helpful, even if it's using a totally different method to achieve the same thing. Thank you so much!
In your situation, how about the following sample script?
Sample script:
function selectDate() {
var sheet = SpreadsheetApp.getActiveSheet();
var searchValue = sheet.getRange("E1").getDisplayValue();
var res = sheet.getRange(3, 1, 1, sheet.getLastColumn()).createTextFinder(searchValue).matchEntireCell(true).findNext();
if (!res) return;
var column = res.getColumn();
sheet.getRange(1, column, sheet.getLastRow()).activate(); // Here, the found column is activated.
Browser.msgBox("Found column number is " + column); // Here, the found column number is shown in a dialog.
}
From your situation, I thought that getRange(3,?,1,ss.getMaxColumns()) in your script might be getRange(3, 1, 1, sheet.getLastColumn()).
When this script is run, row 3 is searched using the value of cell "E1". When the value is found, as a sample, the found column is activated and the column number is shown in a dialog. This is a sample. Please modify this for your actual situation.
Note:
If no column is selected, it is considered that the value of cell "E1" is not found in row 3. At that time, can you provide the detail of your Spreadsheet? By this, I would like to modify it.
Reference:
createTextFinder(findText) of Class Range

Get other value based on active cell

Google sheets / Google app's script
I'm want to know how to get a Cell, based in my current active Cell. Example:
A table where my active Cell is at G15, im wanna get the value of C15 (same row, but C column) and the value of C3 (same column now). To get the logic, imagines if my current Cell change to (example) H7, so, i would get C7 and H3.
I know It maybe is very simple, but im learning, so, im don't very very good ate that.
Try to add into the cell G15 this formula:
=TEXTJOIN(", ", TRUE, $C15, G$3)
And then copy it from G15 to H7, etc.
You can also achieve this using Apps Script:
function returnVals() {
let sheet = SpreadsheetApp.getActiveSheet();
let activeCell = sheet.getActiveCell();
let row = activeCell.getRow();
let col = activeCell.getColumn();
let colCValue = sheet.getRange(row,3).getValue();
let sameCol = sheet.getRange(3,col).getValue();
console.log(colCValue);
console.log(sameCol);
}
The getActiveCell method will return the active cell from the sheet and the getRow and getColumn will get the row and the col of the active cell. Afterwards, based on the row and col retrieved, the function above will simply get the values you need by using the getRange and getValue methods.
So if the active cell is G17, the row will be 17 and the col will be 7; therefore:
colCValue will be the value corresponding to the (17,3) range which is C17;
sameCol will be the value corresponding to the (3, 7) range which is G3.
Reference
getActiveCell();
getRow();
getColumn();
getRange();
getValue().

How to insert a row when cell value changes in a column

How to insert a row with value equal value of first row when cell value is different above cell in column H:H
I believe your goal as follows.
You want to insert a new row and put the header row to the inserted row using Google Apps Script.
In your question, you want to insert the new row when the value of column "F" is changed. But when I saw your sample image, the column is "H" instead of "F".
Unfortunately, I cannot understand about your actual goal. So in this answer, I would like to propose 2 patterns.
From your following replying,
The script is perfect. Can you modify the Text Color is RED and Fill Color is Yellow like a sample.
You want to put the values with the red font color and the yellow background color.
Sample script:
Before you use this script, please set the sheet name. And, in this sample script, the values of column "F" are checked. When you want to check the values of column "H", please modify "F2:F" to "H2:H".
function myFunction() {
const sheetName = "Sheet1"; // Please set the sheetname.
// 1. Retrieve values from the column "F".
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
let [temp, ...values] = sheet.getRange("F2:F" + sheet.getLastRow()).getValues();
// 2. Create an array including the row numbers for inserting new rows.
const insertRows = values.reduce((ar, [v], i) => {
if (v != temp) {
ar.push(i + 3);
temp = v;
}
return ar;
}, []);
// 3. Retrieve header row.
const header = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
// 4. Insert new rows and put the header row to the inserted row.
insertRows.reverse().forEach(i => {
sheet.insertRows(i);
sheet.getRange(i, 1, 1, header[0].length).setValues(header).setBackground("yellow").setFontColor("red"); // Modified
});
}
Note:
This sample script is used for your question and the sample image. So when you changed the structure of your Spreadsheet, the script might not be able to be used. Please be careful this.
Reference:
insertRows(rowIndex)

Google Scripts Concatenate Cells in different columns

I have the following scenario where I want to concatenate cells in different columns but the quantity of columns differs from case to case.
example1: In cell A2 there is the value of 70286 & and the cell B2 there is the value of playmobil.
The end result in F2 should be +70286 +playmobil
example2: In cell A4 there is the value of 70666, in the cell B4 there is the value of lego and in the cell C4 there is the value of ninjago.
The end result in F2 should be +70666 +lego +ninjago
Solutions:
Google Sheet Formula:
You can use TEXTJOIN and if you want a plus sign in front of the string you can use CONCAT to add it.
Use this formula in column F:
=CONCAT("+",textjoin(" +", 1, A2:E2))
If you don't know how many columns you want to use, then you can select the full row:
=CONCAT("+",textjoin(" +", 1, A2:2))
Google Apps Script:
We get the data of the second row as a 2D array using getValues().
Then we apply flat() to convert it to 1D.
The next step is to remove the empty cells by using filter().
Finally, we use reduce() to get the final string as the sum of all strings.
The logic can be easily adjusted. You can do this logic for multiple cells etc.
Code snippet:
function myFunction() {
const sh = SpreadsheetApp.getActive().getSheetByName('Sheet1');
const row = 2;
const data=sh.getRange(row,1,1,sh.getMaxColumns()).
getValues().
flat().
filter(c=>c!='').
reduce( (a, b) => `${a} +${b}`);
const result = `+${data}`;
sh.getRange('F1').setValue(result);
Logger.log(result);
}

Merge cells with same words

I created an array containing multiple rows and columns.
In column B, I have the type of vegetables, and in column c, the varieties.
In the array, there is an automatic sorting which is carried out in column B then column c.
Is it possible to merge automatically the cells of column B containing the same type?
I just know how to merge a range with :
var range1 = sheet.getRange("b4:b9");
range1.merge();
A test array here
Cordially.
How about this sample script? I think that there are several answers for this situation, so please think of this as one of them. The flow of this script is as follows.
Flow :
Retrieve values of column B.
Retrieve the number of duplication values.
Merge cells.
Sample script :
function myFunction() {
var start = 4; // Start row number for values.
var c = {};
var k = "";
var offset = 0;
var ss = SpreadsheetApp.getActiveSheet();
// Retrieve values of column B.
var data = ss.getRange(start, 2, ss.getLastRow(), 1).getValues().filter(String);
// Retrieve the number of duplication values.
data.forEach(function(e){c[e[0]] = c[e[0]] ? c[e[0]] + 1 : 1;});
// Merge cells.
data.forEach(function(e){
if (k != e[0]) {
ss.getRange(start + offset, 2, c[e[0]], 1).merge();
offset += c[e[0]];
}
k = e[0];
});
}
Result :
Note :
This sample script supposes that the values of column B is sorted.
From your shared spreadsheet, it supposes that there are the values for merging cells at column B.
In your shared spreadsheet, the order of sorted values is Composées, Cucurbitacées, Légumineuses, Liliacées, Solanacées. On the other hand, the order of "Wish" is Composées, Cucurbitacées, Légumineuses, Solanacées, Liliacées.
I couldn't understand the difference logic between Liliacées, Solanacées and Solanacées, Liliacées.
In this sample script, it uses the order of sorted values.
Reference :
merge()
If I misunderstand your question, I'm sorry.
Edit 1 :
For your next question, I think that the following flow can achieve what you want. But I think that there may be other solution.
Add values by an user.
Break the merged cells using breakApart().
For example, it merges cells of "A1:A3" which have the values of "sample", "sample", "sample". When this merged cell is broken using breakApart(), each value of "A1:A3" retrieved by getValues() becomes [["sample"],[""],[""]].
Fill the empty cells created by breakApart().
Sort the cells.
Run the sample script on my answer.
Reference :
breakApart()
Edit 2 :
Usage of breakApart():
If you want to break "B1:B", please use as follows.
var ss = SpreadsheetApp.getActiveSheet();
ss.getRange("B1:B").breakApart()