How can I scan a Kendo Grid and get each rows data? - kendo-grid

How can I scan a Kendo Grid and retrieve each row and its data.
I am trying the following but can't seem to see the data :
var grid = $("#mygrid");
var columns = $("#mygrid").data("kendoGrid").columns;
var rows = $("#mygrid").data("kendoGrid").dataSource.data().length;
var noOfCols = columns.length;
var allrows = $("#mygrid").data("kendoGrid").items();
$("#mygrid").data("kendoGrid").items().each(function (a) {
alert(a.text());
});
I need to check the value of a column in each row and if it is not a certain value the report tot the user.
Thanks

Try this:
Get Kendo Grid Selected Cell value:
var grid = $('#GridName').data("kendoGrid");
var selectedItem = grid.dataItem(grid.select());
var selectedValue = selectedItem.YourPropertyColumnName;
For selecting a row by index number use the following:
$('#GridMaster').data("kendoGrid").bind('dataBound', function (e) {
this.element.find('tbody tr:eq(3)').addClass('k-state-selected')
//or >>>
var index = 3;
this.element.find('tbody tr:eq(' + index + ')').addClass('k-state-selected')
});
Alternatively, in order to display the first row always "selected" in Kendo Grid:
$(function () {
$('#GridMaster').data("kendoGrid").bind('dataBound', function (e) {
this.element.find('tbody tr:first').addClass('k-state-selected')
});
});

Related

Google Apps Script - Better way to do a Vlookup

I am doing a kind of VLOOKUP operation in a column with about 3K cells. I am using the following function to do it. I commented on what the code is doing in the function, but to summarize:
It creates a map from values to search for from a table with metadata
It iterates each value of a given range, and searches for coincidences in the previous map
If coincidences are found, it uses the index to capture the second column of the metadata table
Finally, sets the value captured in another cell
This is the code:
function questions_categories() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("data_processed");
// get metadata. This will work as the table to look into
// Column B contains the matching element
// Column C contains the string to return
var metadata = ss.getSheetByName("metadata").getRange('B2:C').getValues()
// Just get the different values from the column B
var dataList = metadata.map(x => x[0])
// Used to define the last cell where to apply the vlookup
var Avals = sheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
// define the range to apply the "vlookup"
const questions_range = sheet.getRange("Q2:Q" + Alast);
forEachRangeCell(questions_range, (cell) => {
var searchValue = cell.getValue();
// is the value to search in the dataList we defined previously?
var index = dataList.indexOf(searchValue);
if (index === -1) {
// if not, throw an error
throw new Error('Value not found')
} else {
// if the value is there, use the index in which that appears to get the value of column C
var foundValue = metadata[index][1]
// set the value in two columns to the right
cell.offset(0, 2).setValue(`${foundValue}`);
}
})
}
forEachRangeCell() is a helper function to iterate through the range.
This works very well, but it resolves 3-4 cells per second, which is not very efficient if I need to check thousands of data. I was wondering if there is a more performant way to achieve the same result.
To improve performance, use Range.setValues() instead of Range.setValue(), like this:
function questions_categories() {
const ss = SpreadsheetApp.getActive();
const source = { values: ss.getRange('metadata!B2:C').getValues() };
const target = { range: ss.getRange('data_processed!Q2:Q') };
source.keys = source.values.map(row => row[0]);
target.keys = target.range.getValues().flat();
const result = target.keys.map(key => [source.values[source.keys.indexOf(key)]?.[1]]);
target.range.offset(0, 2).setValues(result);
}
See Apps Script best practices.

Vlookup + indexOf to find values in a CSV via Google App Script without using loop

The main idea is not to need looping to generate a VLOOKUP because it generates a huge slowdown when the amount of data is very large.
To VLOOKUP on data directly in the sheet I do as follows:
function myFunction() {
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var searchValue = s.getRange("Test!A1").getValue();
var data = SpreadsheetApp.openById("XXXXXXXXXXXX").getSheetByName("Test 2");
var dataValues = data.getRange("A1:A").getValues();
var dataList = dataValues.join("ღ").split("ღ");
var index = dataList.indexOf(searchValue);
if (index === -1) {
s.getRange("Test!B1").setValue('off');
} else {
var row = index + 1;
var foundValue = data.getRange("D"+row).getValue();
s.getRange("Test!B1").setValue(foundValue);
}
}
But there is a big problem in this method, because when many different accounts try to access this sheet at the same time, the error type error: could not connect sheet xxxxx appears or causes huge delay sometimes.
So what was the solution I found? Publish spreadsheet pages as CSV so they can be used and this error doesn't happen when many accounts call the same spreadsheet.
Currently, as I haven't found a way to use indexOf using the first column when I import the CSV with several columns of data, I had to create a spreadsheet page only with the copy data of column A, and then I got to the final result of VLOOKUP like this:
(the value in var searchValue in this example case will be two)
function myFunction() {
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var url_columnA = 'AAAAA';
var url_vlookup = 'BBBBB';
var dataSearch = Utilities.parseCsv(UrlFetchApp.fetch(url_columnA));
var dataList = dataSearch.join("ღ").split("ღ");
var searchValue = s.getRange("Test!A1").getValue();
var index = dataList.indexOf(searchValue);
if (index === -1) {
s.getRange("Test!B1").setValue('off');
} else {
var row = index;
var dataVlookup = Utilities.parseCsv(UrlFetchApp.fetch(url_vlookup));
var foundValue = dataVlookup[row][3];
s.getRange("Test!B1").setValue(foundValue);
}
}
Return example:
other number
var url_vlookup:
Col A
Col B
Col C
Col D
home
1
a
win
away
2
b
loose
one
3
c
number
two
4
d
other number
three
5
e
number again?
var url_columnA:
Col A
home
away
one
two
three
Is there any way to handle var url_vlookup data for search the value in column A so that it's not necessary to use this page var url_columnA separated or is the only way to do it without looping?
The first column can easily be separated after parsing using Array.map:
const dataVlookup = Utilities.parseCsv(UrlFetchApp.fetch(url_vlookup));
const url_columnA = dataVlookup.map(row => row[0])

GAS - Get Certain columns in 2D array based on list

I am trying to build a custom array with Google sheet data by getting certain columns in a certain order. The columns/order is set on a google sheet. The below works but I would like to make the return on row 11 more dynamic.
function buildArray() {
const s = SpreadsheetApp;
const ss = s.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Test Sheet');
const pasteSheet = ss.getSheetByName('Paste');
const data = sheet.getRange(1,1,10,10).getValues(); //update range
// const headers = pasteSheet.getRange(1,1,pasteSheet.getLastRow(),1).getValues().map(function(r){return r}).flat(Infinity); //list of headers needed in 1d array
const colNeed = [0,9,5,6,4,7]; //array index for column numbers
var customArray = data.map(function(r){
return [r[0], r[9], r[5], r[6], r[4], r[7]]; //Would like to make this more dynamic
})
debugger;
pasteSheet.getRange(1, 2, pasteSheet.getLastRow(), pasteSheet.getLastColumn()).clear();
pasteSheet.getRange(1, 2, customArray.length, customArray[0].length).setValues(customArray);
debugger;
}
I have tried replacing the r[0] with r[colNeed.map(...)] and also a for loop with no success.
Any ideas on how to make this work.
Here is a screenshot of the final sheet.
I just copied/pasted the header names in the first column(A) to make sure the right columns were pulled/pasted.
Try this one using a loop to combine the data first then return. Also added the headers on top of customArray.
Code:
function buildArray() {
const s = SpreadsheetApp;
const ss = s.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Test Sheet');
const pasteSheet = ss.getSheetByName('Paste');
const data = sheet.getRange(1,1,10,10).getValues(); // update range
// get headers as single array (excluding blank cells)
// since last column of data can go beyond last row of column A
// headers can include blank cells if there is already data present
const headers = pasteSheet.getRange(1,1,pasteSheet.getLastRow(),1).getValues().flat().filter(Boolean);
// array index for column numbers
const colNeed = [0,9,5,6,4,7];
var customArray = data.map(function(r){
var output = [];
colNeed.forEach(function (col){
output.push(r[col]);
});
return output;
});
// Add headers at the first element of customArray
customArray.unshift(headers)
debugger;
pasteSheet.getRange(1, 2, pasteSheet.getLastRow(), pasteSheet.getLastColumn()).clear();
pasteSheet.getRange(1, 2, customArray.length, customArray[0].length).setValues(customArray);
debugger;
}
Paste:
Test Sheet:
Paste Sheet after executing buildArray:

How do you create a Uniq ID in Google Sheets using apps script?

Im looking at creating a simple Uniq ID after column A has information entered and need for the ID to show on column I. I want to call it Trip Number and display Driver-John0001 and so forth using google sheets script. Sorry I'm new to this so I don't know the lingo
The current code I had found works but now I need it a bit different. This is what my results show Driver:1611710811706
I would like to pull Driver-John0001. Where the name John is generated by the column labeled Driver or column D
How do I change it to add the value on column D + 4 digit numbers that don't repeat?
function TripID () {
{ var sheetVals = [
["DriverLog","Driver:","",9,1]
];}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Loop through every sheet value
for(var i = 0; i < sheetVals.length; i++){
var sheetID = sheetVals[i][0],
frontStr = sheetVals[i][1],
backStr = sheetVals[i][2],
IDcol = sheetVals[i][3],
editCol = sheetVals[i][4];
var offset = IDcol - editCol;
if(sheet.getName() === sheetID){
var date = new Date().getTime();
var newID = frontStr+date+backStr;
//Check the location of the active cell
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() === editCol) {
//Update the ID Column
var cellToChange = selectedCell.offset(0,offset);
if(cellToChange.isBlank()){
cellToChange.setValue(newID);
};
};
};
};
};
Assuming that you will never need more than 9999 IDs, then you could modify your code with the following steps to accomplish this. Note that this solution will allow you to create a (nearly) unlimited number of ID numbers, but after 9999, the length of the id numbers will vary.
Use the PropertiesService to initialize a counter for creating IDs
function createIdProperty() {
PropertiesService.getScriptProperties().setProperty('idCounter', '0');
}
// run this once to create the idCounter property
createIdProperty();
// To test that your property was created you can run this
Logger.log(PropertiesService.getScriptProperties().getProperty('idCounter')); // logs '0'
Create a function to get the next counterId number from Script Properties as a string with 0s replacing empty digits ( 1 => '0001', 2 => '0002', 3 => '0003')
function getUniqueIdNumber() {
var currentIdNum = +PropertiesService.getScriptProperties().getProperty('idCounter'); //get counter as a number
currentIdNum++; // increment counter by 1
currentIdNum+=''; // convert counter back to a string
PropertiesService.getScriptProperties().setProperty('idCounter', currentIdNum); // store the new counter in properies
while (currentIdNum.length < 4) {
currentIdNum = '0'+currentIdNum;
}
return currentIdNum;
}
modify your original code to use getUniqueIdNumber instead of date for creating your id
Change
var newID = frontStr+date+backStr;
To
var idNumber = getUniqueIdNumber();
var newID = frontStr+idNumber;
Note that you can replace frontStr and backStr with any variables you want.

Move Columns Based on Array of Column Numbers

I have an array of columns that I want to keep. I've got that code below:
//Keep only columns user wants.
if(keepColumnsArray != ""){
for (var col = sourceShtLC; col > 0; col--) {
if (keepColumnsArray.indexOf(col) == -1) {
// This isn't a keeper, delete it
destSht.deleteColumn(col);
}
}
}
What I'd like to do now is arrange the columns following the order the keepColumnsArray has them.
Samples:
var keepColumnsArray = [3,2,1,4,5]
Using the above sample I want column 3 to be the first column, column 2 to be the second, column 1 to be the 3rd, column 4 to be the 4th and column 5 to be the 5th.
Current Order:
The order I want it. As you can see it's the same order the array is in.
Solution:
Rather than deleting the columns first I used code from the accepted answer to move the columns I want to keep to the front. In this case Columns 1 through 5 I kept and then I deleted the rest because all that was left were columns I did not need. Here is my final code.
//Use order of array to reorder columns and then delete the rest of the columns
var offset = keepColumnsArray.length;
destSht.insertColumns(1, offset);
keepColumnsArray.forEach(function(e, i) {
destSht.getRange(1, (e + offset), destSht.getLastRow(), 1).copyTo(destSht.getRange(1, i + 1));
});
destSht.deleteColumns(offset + 1, sourceShtLC); //Keep only columns user wants.
You want to arrange the columns on the sheet with var keepColumnsArray = [3,2,1,4,5].
For example, you want to arrange from the columns 1, 2, 3, 4, 5 to 3, 2, 1, 4, 5.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Pattern 1:
In this case, moveColumns is used. The flow of this script is as follows.
Create an array object including the original index and destination index of the columns.
Sort the array.
The columns are arranged using moveColumns.
Sample script:
function myFunction() {
var keepColumnsArray = [3,2,1,4,5];
var obj = keepColumnsArray.reduce(function(ar, e, i) {
ar.push({from: i + 1, to: e});
return ar;
}, []);
obj.sort(function(a, b) {return a.to < b.to ? -1 : 1});
var sheet = SpreadsheetApp.getActiveSheet();
obj.forEach(function(o) {
var columnSpec = sheet.getRange(1, o.from);
if (o.from != o.to) sheet.moveColumns(columnSpec, o.to);
for (var j = 0; j < obj.length; j++) {
if (obj[j].from < o.from) obj[j].from += 1;
}
});
}
Pattern 2:
In this case, each column is copied with the order of keepColumnsArray using a temporal sheet, and put the arranged columns to the original sheet.
Sample script:
function myFunction() {
var keepColumnsArray = [3,2,1,4,5];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var temp = ss.insertSheet("temp");
sheet.activate();
keepColumnsArray.forEach(function(e, i) {
sheet.getRange(1, e, sheet.getLastRow(), 1).copyTo(temp.getRange(1, i + 1));
});
temp.getDataRange().copyTo(sheet.getRange(1, 1));
ss.deleteSheet(temp);
}
Reference:
moveColumns(columnSpec, destinationIndex)
Added:
From OP's comment, In this sample script, the columns are inserted and put the arranged columns.
Instead of creating a temp sheet can we not add 5 columns to the beginning and then copy them to those new columns?
Sample script:
function myFunction() {
var keepColumnsArray = [3,2,1,4,5];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var offset = keepColumnsArray.length;
sheet.insertColumns(1, offset);
keepColumnsArray.forEach(function(e, i) {
sheet.getRange(1, e + offset, sheet.getLastRow(), 1).copyTo(sheet.getRange(1, i + 1));
});
sheet.deleteColumns(offset, offset);
}
Problem
Filtering out columns by criteria
Reordering columns by criteria
Solution
First, add logic that filters and sorts your values. One of the possible algorithms is as follows: map source such that each row is mapped such that each cell is cell by order index from source and filter columns such that its index is a keep index.
var input = [3,2,1,4,5];
var initial = [
['Column1','Column2','Column3','Column4','Column5','Column6','Column7'],
['2first','2second','2third','2fourth','2fifth','2sixth','2seventh'],
];
function filterAndReorder (source,order) {
return source
.map(function(row,r){
return row.map(function(cell,c){
return source[r][order[c]-1];
})
.filter(function(cell,c){
return cell !== undefined;
});
})
}
var output = filterAndReorder(initial,input);
console.log(output);
Then, use the fact that setValues() accepts 2D Array and replaces Range contents:
var lrow = destSht.getLastRow();
var range = destSht.getRange(1,1,lrow,5);
var inputVals = range.getValues();
var keepColumnsArray = []; //obtain the keep Array somehow;
var outputVals = filterAndReorder(inputVals,keepColumnsArray);
var range.setValues(outputVals);
Notes
keepColumnsArray is an Array so the != "" is redundant (unless you actually expect it to be an empty string in which case I would suggest rewriting the logic that outputs the Array - it will save you at least 1 op + save you debug time in the future).
As a general rule of thumb, please, avoid using I/O as much as possible (especially in a loop) and keep input close to start of the logic and output to the end. deleteColumn is an I/O and thus should at least be performed in batch.
UPD if you reorder is partial (that is, there are columns unchanged), you can fold the resulting empty columns via deleteColumns()
References
filter() method ref on MDN
map() method ref on MDN