Using vlookup with repeated interval + split - google-apps-script

I have a list with purchase codes and product serial number and would like to know the purchase price of each product.
In a second list, I have a column with the purchase code, the serial number of a comma-separated product group in another column, and, in the third column, the price of each product group.
I am using the formula below, but when the codes in column "E" are repeated, the formula does not work:
=IF(HLOOKUP(B2;SPLIT(VLOOKUP(A2;$E$1:$F$1000;2;FALSE);", ";TRUE);1;FALSE)<>"#N/A";VLOOKUP(A2;$E$1:$G$5;3;FALSE);"")
Example
Test Spreadsheet

You could use a query
=query(E:G,"select G where E='"&A2&"' and F contains '"&B2&"' limit 1 label G ''")
provided none of the IDs in column B can be a substring of another ID.

I took the liberty of writing the Apps Script code for what you need, this is it:
function myFunction() {
var ss = SpreadsheetApp.getActiveSheet();
var data = ss.getRange("B2:B" + ss.getLastRow()).getValues(); // IDs
var list = ss.getRange("F2:G" + ss.getLastRow()).getValues(); // ID List
var prices = [];
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < list.length; j++){
if (list[j][0].indexOf(data[i][0]) >- 1){
prices.push([list[j][1]]);
break;
}
}
}
ss.getRange("C2:C" + ss.getLastRow()).setValues(prices);
}
it does the check based on the ID List values and the ID itself, it's not doing anything with the cod value.

Related

If cells of Column A are merge, exact cells to be merged in Column E

My sheet consists of details of working hours of crew in shifts. Column A is serial no. Column E is total duty hours. One day duty consists of smaller shifts and some details like S.No, Name, crew id gets repeated.
Initial data
I want to merge column with same cell values (Column A & Column E). I have been able to merge Column A of S.No (thanks to #Tanaike from this Forum) and want to do same thing for Column E.
Achieved so far
What i want
Condition - If Column A is merged, exactly no of cells should merge in Column E. So, if A11, A12 are merged = E11, E12 should merge; A13 not merged = E13 not merged; A14, A15, A16, A17 are merged = E14, E15, E16, E17 should merge.
Thanks.
Relevant Code so far -
// merge columns vertically for same cell value for Column A
var start = 10; //data starts from row 10
var c = {};
var k = "";
var offset = 0;
// Retrieve values of column A
var data = destSheet.getRange(start, 1, lastRow-2, 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]) {
destSheet.getRange(start + offset, 1, c[e[0]], 1).merge();
offset += c[e[0]];
}
k = e[0];
});
Two approaches to solve your issue
Apps Script offers methods like isPartOfMerge() and mergeVertically() which would allow you to transfer the merge formatting from column A to column E.
You can do it by looping through all merged ranges in column A, retreiving their start and end row, and merge the respective ranges in column E:
var range = destSheet.getRange(1,1, destSheet.getLastRow(), 1);
var mergedRanges = range.getMergedRanges();
for (var i = 0; i < mergedRanges.length; i++) {
Logger.log(mergedRanges[i].getA1Notation());
var start = mergedRanges[i].getRow();
var end = mergedRanges[i].getLastRow();
var destinationRange = destSheet.getRange(start, 5, end - start + 1, 1);
destinationRange.mergeVertically();
}
You can copy the formatting from the entire column A to column E - this will copy the merging
To do so, you can use the method copyTo(destination, copyPasteType, transposed) specifying CopyPasteType as PASTE_FORMAT:
var range = destSheet.getRange(1,1, destSheet.getLastRow(), 1);
var destinationRange = destSheet.getRange("E1");
range.copyTo(destinationRange, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);

How to deal with duplicates in google sheets script?

So in the project I want to do I have a google sheet with timestamps and names next to those timestamps in the spreadsheet. I am having trouble accounting for duplicates and giving the name multiple timestamps in another google sheet.
for(var i = 0; i < length; i++){//for loop 1
if(currentCell.isBlank()){//current cell is blank
daySheet.getRange(i+10, 2).setValue(fullName);//set name to first cell
daySheet.getRange(i+10,3).setValue(pI);
daySheet.getRange(i+10,day+3).setValue(1);
}else if(counter > 1 ){//the index of the duplicate in the sheet month
//if counter is > 1 then write duplicates
for(var t = 1; t <= sheetLength ; t++){//loop through sign in sheet
//current index i
if(signInLN == signInSheet.getRange(t+1,3).getValue()){
//if there is a match
daySheet.getRange(t+10,day+3).setValue(1);
//day is equal to the day I spliced from the timestamp
//at this point I am confused on how to get the second date that has the same
//name and add to the row with the original name.
//when i splice the timestamp based on the row of index i, with duplicates I get
//the day number from the first instance where the name is read
}
}
}//for loop 1
How can I get this to work with duplicates so I can account for the dates but make sure that if there are
any duplicates they will be added to the row of the original name
Google Sheet EX:
12/10/2020 test1
12/11/202 test2
12/15/2020 test1
Should be something like this:
name 10 11 12 13 14 15 16
test1 1 1
test2 1
//the one is to identify that the date is when the user signed in on the sheets.
Sample Spreadsheet:
Code snippet done with Apps Script, adapt it to your needs.
use Logger.log() in case you don't understand parts of code
It is done mainly with functional JavaScript
function main(){
var inputRange = "A2:B";
var sheet = SpreadsheetApp.getActive().getSheets()[0]
var input = sheet.getRange(inputRange).getValues(); //Read data into array
var minDate, maxDate;
var presentDates = input.map(function(row) {return row[0];}); //Turns input into an array of only the element 0 (the date)
minDate = presentDates.reduce(function(a,b) { return a<b?a:b}); //For each value, if its the smallest: keep; otherwise: skip;
maxDate = presentDates.reduce(function(a,b) { return a>b?a:b}); //Same as above, but largest.
var dates = [];
for (var currentDate = minDate; currentDate <= maxDate; currentDate.setDate(currentDate.getDate()+1)) {
dates.push(getFormattedDate(currentDate)); //Insert each unique date from minDate to maxDate (to leave no gaps)
}
var uniqueNames = input.map(function(row) {return row[1];}) //Turns input into an array of only the element at 1 (the names)
.filter(function (value, index, self) {return self.indexOf(value) === index;}); //Removes duplicates from the array (Remove the element if the first appearence of it on the array is not the current element's index)
var output = {}; //Temporary dictionary for easier counting later on.
for (var i=0; i< dates.length; i++) {
var dateKey = dates[i];
for (var userIndex = 0; userIndex <= uniqueNames.length; userIndex++) {
var mapKey = uniqueNames[userIndex]+dateKey; //Match each name with each date
input.map(function(row) {return row[1]+getFormattedDate(row[0])}) //Translate input into name + date (for easier filtering)
.filter(function (row) {return row === mapKey}) //Grab all instances where the same date as dateKey is present for the current name
.forEach(function(row){output[mapKey] = (output[mapKey]||0) + 1;}); //Count them.
}
}
var toInsert = []; //Array that will be outputted into sheets
var firstLine = ['names X Dates'].concat(dates); //Initialize with header (first element is hard-coded)
toInsert.push(firstLine); //Insert header line into output.
uniqueNames.forEach(function(name) {
var newLine = [name];
for (var i=0; i< dates.length; i++) { //For each name + date combination, insert the value from the output dictionary.
var currentDate = dates[i];
newLine.push(output[name+currentDate]||0);
}
toInsert.push(newLine); //Insert into the output.
});
sheet.getRange(1, 5, toInsert.length, toInsert[0].length).setValues(toInsert); //Write the output to the sheet
}
// Returns a date in the format MM/dd/YYYY
function getFormattedDate(date) {
var year = date.getFullYear();
var month = (1 + date.getMonth()).toString();
month = month.length > 1 ? month : '0' + month;
var day = date.getDate().toString();
day = day.length > 1 ? day : '0' + day;
return month + '/' + day + '/' + year;
}
Run script results:

Sum up the time values corresponding to same date

In my sheet column A is date and column B is time duration values, I want to find the dates which are repeated and sum up the corresponding time values of the repeated dates and show the sum in the last relevant repeated date. And delete all the other repeated dates. ie if 18/07/2019 is repeated 4 times i have to sum up all the four duration values and display the sum value in the 4th repeated position and delete the first three date 18/07/2019. I have to do this all those dates that are repeated. I have wrote code to my best knowledge
function countDate() {
var data = SpreadsheetApp.getActive();
var sheet = data.getSheetByName("Sheet5");
var lastRow = sheet.getLastRow();
var sh = sheet.getRange('A1:A'+lastRow);
var cell = sh.getValues();
var data= sheet.getRange('B1:B'+lastRow).getValues();
for (var i =0; i < lastRow; ++i){
var count = 0;
var column2 = cell[i][0];
for (var j =0; j < i; j++)
{
var p=0;
var column4 = cell[j][0];
if (column4 - column2 === 0 )
{
var value1 = data[j][0];
var value2 = data[i][0];
var d = value2;
d.setHours(value1.getHours()+value2.getHours()+0);
d.setMinutes(value1.getMinutes()+value2.getMinutes());
sheet.getRange('C'+(i+1)).setValue(d).setNumberFormat("[hh]:mm:ss");
sheet.deleteRow(j+1-p);
p++;
}
}
}
}
The copy of the sheet is shown
column C is the values I obtain through the above code AND column D is the desired value
After computing the sum I need to delete the repeated rows till 15 here
Answer:
You can do this by converting your B-column to a Plain text format and doing some data handling with a JavaScript dictionary.
Code:
function sumThemAllUp() {
var dict = {};
var lastRow = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getLastRow();
var dates = SpreadsheetApp.getActiveSpreadsheet().getRange('A1:A' + lastRow).getValues();
var times = SpreadsheetApp.getActiveSpreadsheet().getRange('B1:B' + lastRow).getValues();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).setNumberFormat("#");
for (var i = 0; i < dates.length; i++) {
if (!dict[dates[i][0]]) {
dict[dates[i][0]] = times[i][0];
}
else {
var temp = dict[dates[i][0]];
var hours = parseInt(temp.split(':')[0]);
var minutes = parseInt(temp.split(':')[1]);
var additionalHours = parseInt(times[i][0].split(':')[0]);
var additionalMinutes = parseInt(times[i][0].split(':')[1]);
var newMinutes = minutes + additionalMinutes;
var newHours = hours + additionalHours;
if (newMinutes > 60) {
newHours = newHours + 1;
newMinutes = newMinutes - 60;
}
dict[dates[i][0]] = newHours + ':' + newMinutes;
}
}
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('A1:B' + lastRow).clear();
var keys = Object.keys(dict);
for (var i = 0; i < keys.length; i++) {
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('A' + (i + 1)).setValue(keys[i]);
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('B' + (i + 1)).setValue(dict[keys[i]]);
}
}
Assumptions I made:
There are a few assumptions I made when writing this, you can edit as needed but I figured I should let you know:
There are only dates in Column A and only times in Column B.
The times in column B are either Hours:Minutes or Minutes:Seconds. Either way, if the value to the right of the : hits 60, it adds one to the left value and resets.
The Sheet within the Spreadsheet is the first sheet; that which is returned by Spreadsheet.getSheets()[0].
References:
w3schools - JavaScript Objects
Spreadsheet.getSheets()
w3schools - JavaScript String split() Method
MDN web docs - parseInt() method
Google Sheets > API v4 - Date and Number Formats

Google Sheets Add row based on cell number value

I'm trying to make a google sheet script that adds a row based on cell value, basically if I have in the Quantity (Column D) 7x laptops, I want the script to add 6 additional rows below if Column H is marked as "Yes" through data validation.
What I was able to find and to do is only duplicate that row but is without data validation and I would prefer to add the data validation and possible make each quantity split to 1 (instead of 7) after the duplication.
`function autoDup() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
var newData = [];
for(var n in data){
newData.push(data[n]);
if(!Number(data[n][3])){continue};// if column 3 is not a number then do nothing
for(var c=1 ; c < Number(data[n][3]) ; c++){ // start from 1 instead of 0 because we have already 1 copy
newData.push(data[n]);//store values
}
}
sheet.getRange(1,1,newData.length,newData[0].length).setValues(newData).sort({column: 1, ascending: false});// write new data to sheet, overwriting old data
}`
Hope someone is able to help me.
Thank you,
Column D contains a qty and goods description. If Column H = "Yes", you want to insert a number of rows below Col D equal to the qty minus one. If Column H <> "Yes, then take no action.
Sample data - Before
Sample data - After
function so5925663201() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "59256632";
var sheet = ss.getSheetByName(sheetname);
var row = 7;
// get value of Column H
var colHValue = sheet.getRange(row,8).getValue();
if (colHValue === "Yes"){
//Logger.log("DEBUG: Col H = yes. do something")
// get value of Column D
var Value = sheet.getRange(row,4).getValue();
var searchterm = "x";
var indexOfFirst = Value.indexOf(searchterm);
//Logger.log("DEBUG: the first instance of 'x' is "+indexOfFirst);
// get the quantity and convert from a string to a number
var qty = Value.substring(0, indexOfFirst);
var qtynum = +qty;
// var newtype = typeof qtynum; // DEBUG
//Logger.log("DEBUG: the quantity is "+qtynum+", new type = "+newtype)
// This inserts rows after
sheet.insertRowsAfter(row, qtynum-1);
}
else{
//Logger.log("DEBUG: col H <> Yes. do nothing");
}
}

Issue copying invoice data from one sheet to another when payment is selected

I am looking to save products that are chosen in an invoice to another sheet in the same workbook when a payment method is selected. Here is a copy of the sheet. How the sheet works:
1) User places "x" into selection column in "Protocol Selection" (WORKING)
2) In the next sheet in the workbook "Patient Invoice", an invoice with bottle count is generated (WORKING)
3) I want the past invoices (with product, date, pill-count, etc.) to be copied over to the "Past Invoices" sheet when the "Method of Payment" is selected. This is a drop-down Data Validation cell. (NOT WORKING)
Is there a way to do this without a custom script? IF not, what is the script?
Probably not the most efficient method but it does work...
Script
function makePastInvoice() {
var patientInvoice = SpreadsheetApp.getActive().getSheetByName('Patient Invoice');
var pastInvoices = SpreadsheetApp.getActive().getSheetByName('Past Invoices');
// vistor details
var visitDate = patientInvoice.getRange("D3").getValue();
var patientName = patientInvoice.getRange("G5").getValue();
var doctorName = patientInvoice.getRange("I5").getValue();
var programLength = patientInvoice.getRange("I3").getValue();
var invoiceNumber = patientInvoice.getRange("G3").getValue();
var total = patientInvoice.getRange("K30").getValue();
var paymentMethod = patientInvoice.getRange("D5").getValue();
var visitorDetails = [visitDate, patientName, doctorName, programLength, invoiceNumber, total, paymentMethod];
var lastRow = pastInvoices.getLastRow();
var currentRow = lastRow + 1;
// set values of patient details to Past Invoices sheet
for (var i = 0; i < visitorDetails.length; i++) {
pastInvoices.getRange(currentRow, i+1).setValue(visitorDetails[i]);
}
// 15 possible max items (per Past Invoices)
var productIDs = patientInvoice.getRange("A8:A22").getValues();
// count number of products
var productCount = 0;
for(var i = 0; i < productIDs.length; i++) {
if (productIDs[i] > "") {
productCount++;
}
}
// Patient Invoice
var firstItemRow = 8;
// Past Invoices
var firstItemCol = 8 // column H
// loop through purchases
for(var i = 0; i < productCount; i++) {
// purchase details
var productName = patientInvoice.getRange(firstItemRow, 2).getValue(); // col B
var dosage = patientInvoice.getRange(firstItemRow, 5).getValue(); // col E
var numBottles = patientInvoice.getRange(firstItemRow, 6).getValue(); // col F
var purchaseDetails = [productName, dosage, numBottles];
// set values of purchase details to Past Invoices sheet
for(var j = 0; j < purchaseDetails.length; j++) {
pastInvoices.getRange(currentRow, firstItemCol).setValue(purchaseDetails[j])
firstItemCol ++;
}
firstItemRow++;
}
}
Trigger
I'm sure there's a way to trigger the script when the payment method changes or any other method of choice but I created a "submit" trigger with a drawing and assigned it to the makePastInvoice function.
Also, you'll probably want to change the "Visit Date" column's format to date.