How do you overwrite data instead of appendRow? - google-apps-script

Currently the code below appends new data to my spreadsheet every time it runs and I have to manually remove the data before running.
{
var ss = SpreadsheetApp.openById("blank");
var sheetName = ss.getSheetByName("Team Database");
var url = "blank";
var response = JSON.parse(UrlFetchApp.fetch(url));
var current = 1;
if(response.returnData && response.returnData.equipos.length > 0)
{
for(var team in response.returnData.equipos)
{
if(response.returnData.equipos[team].members.length > 0)
{
var i = 0;
while(i < response.returnData.equipos[team].members.length)
{
console.log(response.returnData.equipos[team].name);
console.log(response.returnData.equipos[team].members[i].userId);
sheetName.appendRow([response.returnData.equipos[team].name, response.returnData.equipos[team].members[i].userId]);
i++;
}
}
}
}
if(team.length > 0)
{
sheetName.getRange('D2').setValue('=NOW()');
sheetName.getRange('D1').setValue(sheetName.getRange('D2').getValue());
sheetName.getRange('D2').clear();
}
}
Is there a function that would overwrite it instead like: sheetName.overwriteRow([response.returnData.equipos[team].name, response.returnData.equipos[team].members[i].userId]);?

function myfunc() {
var ss = SpreadsheetApp.openById("blank");
var sh = ss.getSheetByName("Team Database");
var url = "blank";
var response = JSON.parse(UrlFetchApp.fetch(url));
var current = 1;
let oA = [];
if (response.returnData && response.returnData.equipos.length > 0) {
for (var team in response.returnData.equipos) {
if (response.returnData.equipos[team].members.length > 0) {
var i = 0;
while (i < response.returnData.equipos[team].members.length) {
console.log(response.returnData.equipos[team].name);
console.log(response.returnData.equipos[team].members[i].userId);
oA.push([response.returnData.equipos[team].name, response.returnData.equipos[team].members[i].userId]);
i++;
}
}
}
}
if (team.length > 0) {
sh.getRange('D2').setValue('=NOW()');
sh.getRange('D1').setValue(sh.getRange('D2').getValue());
sh.getRange('D2').clear();
sh.getRange(3,1,sh.getLastRow() - 2, sh.getLastColumn()).clearContent();
sh.getRange(3,1,oA.length, oA[0].length).setValues(oA);
}
}

Related

renameCustomField() Change the Label Field Name

SVP i'm newsbe in Appsscript with API PEOPLE
This script don't work : I want to rename "CKD EPI" by "CKD EPI / CROKOFF / CREAT"
Can you tell me what's wrong ?
function renameCustomField() {
var debug = true;
var group = People.PeopleApi.getContactGroups().get("Mygroup").execute();
var oldFieldName = "CKD EPI";
var newFieldName = "CKD EPI / CROKOFF / CREAT";
for (var i = 0; i < group.members.length; i++) {
var contact = group.members[i];
var fields = contact.userDefined;
for (var j = 0; j < fields.length; j++) {
if (fields[j].key === oldFieldName) {
fields[j].key = newFieldName;
if (debug) {
console.log("Renaming field for contact: " + contact.names[0].displayName);
}
}
}
var updateContact = {
resourceName: contact.resourceName,
updatePersonFields: "userDefined",
userDefined: fields
}
People.PeopleApi.updateContact(updateContact).execute();
}
}

My website becomes unresponsive when dealing 1000 rows excel file

I am uploading data from an excel file into my website using input html button.
and then convert the data into json and then I map it with local external metadata.
Finally view it using the id.
My website becomes unresponsive & sometimes takes a lot of time processing. Please help
function ExportToTable() {
var regex = /^([a-zA-Z0-9\s_\\.\-:()])+(.xlsx|.xls)$/;
/*Checks whether the file is a valid excel file*/
if (regex.test($("#excelfile").val().toLowerCase())) {
var xlsxflag = false; /*Flag for checking whether excel is .xls format or .xlsx format*/
if ($("#excelfile").val().toLowerCase().indexOf(".xlsx") > 0) {
xlsxflag = true;
}
/*Checks whether the browser supports HTML5*/
if (typeof (FileReader) != "undefined") {
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
/*Converts the excel data in to object*/
if (xlsxflag) {
var workbook = XLSX.read(data, { type: 'binary' });
}
else {
var workbook = XLS.read(data, { type: 'binary' });
}
/*Gets all the sheetnames of excel in to a variable*/
var sheet_name_list = workbook.SheetNames;
console.log(sheet_name_list);
var cnt = 0; /*This is used for restricting the script to consider only first
sheet of excel*/
sheet_name_list.forEach(function (y) { /*Iterate through all sheets*/
/*Convert the cell value to Json*/
if (xlsxflag) {
var exceljson = XLSX.utils.sheet_to_json(workbook.Sheets[y]);
}
else {
var exceljson = XLS.utils.sheet_to_row_object_array(workbook.Sheets[y]);
}
//Download & View Subscriptions
if (exceljson.length > 0 && cnt == 1) {
metadata = [];
fetch("metadata.json")
.then(response => response.json())
.then(json => {
metadata = json;
console.log(metadata);
user_metadata1 = [], obj_m_processed = [];
for (var i in exceljson) {
var obj = { email: exceljson[i].email, name: exceljson[i].team_alias, id: exceljson[i].autodesk_id };
for (var j in metadata) {
if (exceljson[i].email == metadata[j].email) {
obj.GEO = metadata[j].GEO;
obj.COUNTRY = metadata[j].COUNTRY;
obj.CITY = metadata[j].CITY;
obj.PROJECT = metadata[j].PROJECT;
obj.DEPARTMENT = metadata[j].DEPARTMENT;
obj.CC=metadata[j].CC;
obj_m_processed[metadata[j].email] = true;
}
}
obj.GEO = obj.GEO || '-';
obj.COUNTRY = obj.COUNTRY || '-';
obj.CITY = obj.CITY || '-';
obj.PROJECT = obj.PROJECT || '-';
obj.DEPARTMENT = obj.DEPARTMENT || '-';
obj.CC = obj.CC || '-';
user_metadata1.push(obj);
}
for (var j in metadata) {
if (typeof obj_m_processed[metadata[j].email] == 'undefined') {
user_metadata1.push({ email: metadata[j].email, name: metadata[j].name, id: metadata[j].autodesk_id,
GEO: metadata[j].GEO,
COUNTRY : metadata[j].COUNTRY,
CITY : metadata[j].CITY,
PROJECT : metadata[j].PROJECT,
DEPARTMENT : metadata[j].DEPARTMENT,
CC:metadata[j].CC
});
}
}
document.getElementById("headings4").innerHTML = "MetaData Mapping";
BindTable(user_metadata1, '#user_metadata1
cnt++;
});
$('#exceltable').show();
}
if (xlsxflag) {/*If excel file is .xlsx extension than creates a Array Buffer from excel*/
reader.readAsArrayBuffer($("#excelfile")[0].files[0]);
}
else {
reader.readAsBinaryString($("#excelfile")[0].files[0]);
}
}
else {
alert("Sorry! Your browser does not support HTML5!");
}
}
else {
alert("Please upload a valid Excel file!");
}
}
Here is how the json is bind after mapping metadata
function BindTable(jsondata, tableid) {/*Function used to convert the JSON array to Html Table*/
var columns = BindTableHeader(jsondata, tableid); /*Gets all the column headings of Excel*/
for (var i = 0; i < jsondata.length; i++) {
var row$ = $('<tr/>');
for (var colIndex = 0; colIndex < columns.length; colIndex++) {
var cellValue = jsondata[i][columns[colIndex]];
if (cellValue == null)
cellValue = "";
row$.append($('<td/>').html(cellValue));
}
$(tableid).append(row$);
}
}
function BindTableHeader(jsondata, tableid) {/*Function used to get all column names from JSON and bind the html table header*/
var columnSet = [];
var headerTr$ = $('<tr/>');
for (var i = 0; i < jsondata.length; i++) {
var rowHash = jsondata[i];
for (var key in rowHash) {
if (rowHash.hasOwnProperty(key)) {
if ($.inArray(key, columnSet) == -1) {/*Adding each unique column names to a variable array*/
columnSet.push(key);
headerTr$.append($('<th/>').html(key));
}
}
}
}
$(tableid).append(headerTr$);
return columnSet;
}

GAS insert emoji into a table

I am sending an email that contains a table.
The output is a table in the email where the first column should be the emoji follow by the name and date.
When I tried to put the emoji into the column of my array, the output in the column 1 of the table is a black ? triangle.
My code snippet is below -
for (let indx =0; indx<empDates.length; ++indx){
if (empDates[indx][startMonthIndx]!="") {
var tempDate = new Date(empDates[indx][startMonthIndx+1]);
if (tempDate.getMonth() >= monDate.getMonth() && tempDate.getDate() >= monDate.getDate() && tempDate.getMonth() <= sunDate.getMonth() && tempDate.getDate()<= sunDate.getDate()) {
var emojiType = empDates[indx][startMonthIndx];
if (emojiType.indexOf("(B)")==0) {
var emojiIcon = '🎂';
} else {
var emojiIcon = '🎉';
}
thisWeekReminder.push(emojiIcon, [empDates[indx][startMonthIndx], empDates[indx][startMonthIndx+1]]);
}
if (startMonth != endMonth) {
tempDate = new Date(empDates[indx][startMonthIndx+3]);
if (empDates[indx][startMonthIndx+2]!="") {
if (tempDate.getMonth() >= monDate.getMonth() && tempDate.getDate() >= monDate.getDate() && tempDate.getMonth() <= sunDate.getMonth() && tempDate.getDate()<= sunDate.getDate()) {
var emojiType = empDates[indx][startMonthIndx];
if (emojiType.indexOf("(B)")==0) {
var emojiIcon = '🎂';
} else {
var emojiIcon = '🎉';
}
thisWeekReminder.push(emojiIcon, [empDates[indx][startMonthIndx+2], empDates[indx][startMonthIndx+3]]);
}
}
}
}
}
const htmlTemplate = HtmlService.createTemplateFromFile('emailTable');
htmlTemplate.name = name;
htmlTemplate.specialDate = specialDate;
htmlTemplate.tablerangeValue = thisWeekReminder;
const htmlForEmail = htmlTemplate.evaluate().getContent();
var toEmail = emailAddr[0][0];
var ccEmail = "";
for (let i=1; i<emailAddr.length; ++i) {
if (emailAddr[i][0] != "") {
if (i=1) {
ccEmail = emailAddr[i][0];
} else {
ccEmail = ccEmail + "," + emailAddr[i][0];
}
}
}
MailApp.sendEmail({
to: toEmail,
cc: ccEmail,
subject: "REMINDER: This week's Birthdays and Anniversaries",
htmlBody:htmlForEmail
});
}
}

Why google spreadsheets scripts can't sort?

function SUMMARIZE_TOGGL_ENTRIES(proj, desc, time, taskDates, uID) {
if (!desc.map || !time.map || !taskDates.map || !proj.map) {
return 'INPUT HAS TO BE MAP';
}
if (desc.length != time.length || time.length != taskDates.length || proj.length != taskDates.length) {
return 'WRONG INPUT ARRAYS LEN';
}
var resObj = {'NO_DESCRIPTION': '00:00:00'};
var keys = [];
var result = [];
for (var i = 1; i < desc.length; i++) {
var tmpKey = createKey(proj[i], desc[i], uID[i]);
console.log('tmpKey',tmpKey);
if (resObj[tmpKey]) {
resObj[tmpKey] = formatTime(timestrToSec(resObj[tmpKey]) + timestrToSec(time[i]));
} else {
resObj[tmpKey] = formatTime(timestrToSec(time[i]));
}
}
keys = Object.keys(resObj);
var b=0;
for (b; b < keys.length; b++) {
var tmp = [];
var key = keys[b].split('__')[1];
console.log('keys',keys[b].split('__'));
tmp.push(keys[b].split('__')[0]);
tmp.push(key);
tmp.push(resObj[keys[b]]);
tmp.push(taskLastDate(keys[b], desc, taskDates, proj, uID))
tmp.push(keys[b].split('__')[2]);
if(tmp[3] != '01-01-1991 01:01:01' && tmp[3] != ''){
result.push(tmp);
}
}
return result.sort(function (a,b){
var ad = new Date(a[3].split(' ')[0].replace(/-/g,'.'));
var bd = new Date(b[3].split(' ')[0].replace(/-/g,'.'));
if (ad > bd) {
return 1;
}
if (ad < bd) {
return -1;
}
return 0;
});
}
as you can see I'm trying to return a sorted array of arrays. But it don't returns the same array. If i lunch this func on my PC it works. so what's the problem? Does anyone have an idea?
result image
Seems like your sort function will always return 0. Change to
return result.sort(function (a,b){
var ad = new Date(a[3].split(' ')[0].replace(/-/g,'.'));
var bd = new Date(b[3].split(' ')[0].replace(/-/g,'.'));
if (ad > bd) {
return 1;
} else if (ad < bd) {
return -1;
} else {
return 0;
}
});
and see if that works?

Need to optimise Google Apps Script to reduce script runtime

I've constructed a "Conveyor" style system within Google Sheets to move users from further slots into closer slots when they become available.
Screenshot of the Google Sheets layout where this is being implemented.
For example, if someone was in "User 1" but then left, it moves the user in "User 2" into "User 1", the user in "User 3" moves to "User 2", etc., pushing them along the chain, ensuring that the slots are used in order.
As the requirements are time-based, it's often the case that Users 1, 2 and 3 are removed at the same time, meaning that one person is left in the "User 4" slot. Using the current script, it will move them from "User 4" -> "User 3" -> "User 2" -> "User 1".
Additionally, due to some formatting I have set up (Index Matching), I need the cells to display the word "None" instead of staying blank. I've been able to do this easily by checking if the cell is blank, and if so, setting the value to "None".
The script works, but it is really slow. The total script execution time is over 14 seconds.
Here's the scripting I'm using:
function conveyor() {
var s = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('UsernameSheet');
// LOOP 1
var Username1Val = s.getRange('C3').getDisplayValue();
var Username2Val = s.getRange('I3').getDisplayValue();
var Username3Val = s.getRange('O3').getDisplayValue();
var Username4Val = s.getRange('U3').getDisplayValue();
var Username1 = s.getRange('C3')
var Username2 = s.getRange('I3')
var Username3 = s.getRange('O3')
var Username4 = s.getRange('U3')
if (Username3Val == 'None') {
FourtoThree();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
if (Username2Val == 'None') {
ThreetoTwo();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
if (Username1Val == 'None') {
TwotoOne();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
// LOOP 2
var Username1Val = s.getRange('C3').getDisplayValue();
var Username2Val = s.getRange('I3').getDisplayValue();
var Username3Val = s.getRange('O3').getDisplayValue();
var Username4Val = s.getRange('U3').getDisplayValue();
var Username1 = s.getRange('C3')
var Username2 = s.getRange('I3')
var Username3 = s.getRange('O3')
var Username4 = s.getRange('U3')
if (Username3Val == 'None') {
FourtoThree();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
if (Username2Val == 'None') {
ThreetoTwo();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
if (Username1Val == 'None') {
TwotoOne();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
// LOOP 3
var Username1Val = s.getRange('C3').getDisplayValue();
var Username2Val = s.getRange('I3').getDisplayValue();
var Username3Val = s.getRange('O3').getDisplayValue();
var Username4Val = s.getRange('U3').getDisplayValue();
var Username1 = s.getRange('C3')
var Username2 = s.getRange('I3')
var Username3 = s.getRange('O3')
var Username4 = s.getRange('U3')
if (Username3Val == 'None') {
FourtoThree();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
if (Username2Val == 'None') {
ThreetoTwo();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
if (Username1Val == 'None') {
TwotoOne();
}
if (Username1.isBlank()) {
Username1.setValue('None');
}
if (Username2.isBlank()) {
Username2.setValue('None');
}
if (Username3.isBlank()) {
Username3.setValue('None');
}
if (Username4.isBlank()) {
Username4.setValue('None');
}
}
function FourtoThree() {
var s = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('UsernameSheet');
var Username3Name = s.getRange('O3');
var Username3NameVal = s.getRange('O3').getDisplayValue();
var Username3Start = s.getRange('O4');
var Username3StartVal = s.getRange('O4').getDisplayValue();
var Username3Finish = s.getRange('O5');
var Username3FinishVal = s.getRange('O5').getDisplayValue();
var Username3Break = s.getRange('Q7');
var Username3BreakVal = s.getRange('Q7').getDisplayValue();
var Username3Busy = s.getRange('Q6');
var Username3BusyVal = s.getRange('Q6').getDisplayValue();
var Username4Name = s.getRange('U3');
var Username4NameVal = s.getRange('U3').getDisplayValue();
var Username4Start = s.getRange('U4');
var Username4StartVal = s.getRange('U4').getDisplayValue();
var Username4Finish = s.getRange('U5');
var Username4FinishVal = s.getRange('U5').getDisplayValue();
var Username4Break = s.getRange('W7');
var Username4BreakVal = s.getRange('W7').getDisplayValue();
var Username4Busy = s.getRange('W6');
var Username4BusyVal = s.getRange('W6').getDisplayValue();
Username3Name.setValue(Username4NameVal);
Username3Start.setValue(Username4StartVal);
Username3Finish.setValue(Username4FinishVal);
Username3Break.setValue(Username4BreakVal);
Username3Busy.setValue(Username4BusyVal);
Username4Name.clearContent();
Username4Start.clearContent();
Username4Finish.clearContent();
Username4Break.clearContent();
Username4Busy.clearContent();
}
function ThreetoTwo() {
var s = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('UsernameSheet');
var Username2Name = s.getRange('I3');
var Username2NameVal = s.getRange('I3').getDisplayValue();
var Username2Start = s.getRange('I4');
var Username2StartVal = s.getRange('I4').getDisplayValue();
var Username2Finish = s.getRange('I5');
var Username2FinishVal = s.getRange('I5').getDisplayValue();
var Username2Break = s.getRange('K7');
var Username2BreakVal = s.getRange('K7').getDisplayValue();
var Username2Busy = s.getRange('K6');
var Username2BusyVal = s.getRange('K6').getDisplayValue();
var Username3Name = s.getRange('O3');
var Username3NameVal = s.getRange('O3').getDisplayValue();
var Username3Start = s.getRange('O4');
var Username3StartVal = s.getRange('O4').getDisplayValue();
var Username3Finish = s.getRange('O5');
var Username3FinishVal = s.getRange('O5').getDisplayValue();
var Username3Break = s.getRange('Q7');
var Username3BreakVal = s.getRange('Q7').getDisplayValue();
var Username3Busy = s.getRange('Q6');
var Username3BusyVal = s.getRange('Q6').getDisplayValue();
Username2Name.setValue(Username3NameVal);
Username2Start.setValue(Username3StartVal);
Username2Finish.setValue(Username3FinishVal);
Username2Break.setValue(Username3BreakVal);
Username2Busy.setValue(Username3BusyVal);
Username3Name.clearContent();
Username3Start.clearContent();
Username3Finish.clearContent();
Username3Break.clearContent();
Username3Busy.clearContent();
}
function TwotoOne() {
var s = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('UsernameSheet');
var Username1Name = s.getRange('C3');
var Username1NameVal = s.getRange('C3').getDisplayValue();
var Username1Start = s.getRange('C4');
var Username1StartVal = s.getRange('C4').getDisplayValue();
var Username1Finish = s.getRange('C5');
var Username1FinishVal = s.getRange('C5').getDisplayValue();
var Username1Break = s.getRange('E7');
var Username1BreakVal = s.getRange('E7').getDisplayValue();
var Username1Busy = s.getRange('E6');
var Username1BusyVal = s.getRange('E6').getDisplayValue();
var Username2Name = s.getRange('I3');
var Username2NameVal = s.getRange('I3').getDisplayValue();
var Username2Start = s.getRange('I4');
var Username2StartVal = s.getRange('I4').getDisplayValue();
var Username2Finish = s.getRange('I5');
var Username2FinishVal = s.getRange('I5').getDisplayValue();
var Username2Break = s.getRange('K7');
var Username2BreakVal = s.getRange('K7').getDisplayValue();
var Username2Busy = s.getRange('K6');
var Username2BusyVal = s.getRange('K6').getDisplayValue();
Username1Name.setValue(Username2NameVal);
Username1Start.setValue(Username2StartVal);
Username1Finish.setValue(Username2FinishVal);
Username1Break.setValue(Username2BreakVal);
Username1Busy.setValue(Username2BusyVal);
Username2Name.clearContent();
Username2Start.clearContent();
Username2Finish.clearContent();
Username2Break.clearContent();
Username2Busy.clearContent();
}
What the script does is Firstly runs to check if the usernames 1-4 have a blank cell. If they do, they set the values to "None".
Then, an IF statement runs to check if Username 3 = None, if it does, it then runs function FourtoThree, which in itself moves the values from Username 4 to Username 3. It then checks Username 2, then username 1, and does the same type of functions of moving the data along if the value is "None". This whole function then runs 3 times, the maximum number of moves the conveyor system would need to complete.
I'm still a novice with Google Apps Script (well, JavaScript in general), so I threw this together and was happy it worked.
Now, the sluggish runtime of the script is becoming problematic for its use-scenario , and I'm looking for ways to optimize this, please.
I'm not at all expecting anyone to write me a script that would alleviate my issues, but if anyone could suggest an example of a function that could achieve what I'm trying to do, and/or an idea of how to implement it.
Here is a prototype you can try but I don't use "None" just blank
function conveyor() {
var s = SpreadsheetApp.getActiveSpreadsheet();
// Get range C3:W7
var r = s.getSheetByName('Test').getRange(3,3,5,21);
var users = r.getValues();
var formulas = r.getFormulas();
for( var i=0; i<13; i+=6 ) { // Only do it 3 times
if( users[0][i] === "" ) {
users[0][i] = users[0][i+6]; // Username
users[0][i+6] = "";
users[1][i] = users[1][i+6]; // Start time
users[1][i+6] = "";
users[2][i] = users[2][i+6]; // Finish time
users[2][i+6] = "";
users[3][i+2] = users[3][i+8]; // Busy
users[3][i+8] = false;
users[4][i+2] = users[4][i+8]; // Break
users[4][i+8] = false;
}
}
for( var i=0; i<users.length; i++ ) {
for( var j=0; j<users[0].length; j++ ) {
if( formulas[i][j] !== "" ) users[i][j] = formulas[i][j].toString();
}
}
r.setValues(users);
}