I'm receiving data from a websocket (live stream), and trying to put it into a table. I'm currently using the following code:
var table = document.getElementById("websocket-data");
function writeToScreen(message) {
var new_row = table.insertRow(0);
var cell1 = new_row.insertCell(0);
var cell2 = new_row.insertCell(1);
var obj = JSON.parse(message.data);
console.log(obj.value);
cell1.innerHTML = obj.id;
cell2.innerHTML = obj.value;
}
This works, and creates a new row for every JSON packet. The functionality that I am looking for is: On receipt of a JSON, if the id is not in the table, then create a row with id and value, however, if the id is already in the table, simply update the value. I've come across a few ways of doing this, but I'd like to know what the 'proper' way to do it is. I was thinking that perhaps the data should go into an array, and then the array should populate the table, but that would involve repopulating the entire table every time the array changed... I'm happy to use JQuery or similar if necessary.
You could use an array and repopulate the table every time like you said, and if the table will only ever be small then you may not run into issues with that.
One possible alternative of many is maintaining an object in the background with your ids as keys and then store the value and the table row index as values.
Something like:
var tableStore = {};
function recieveMessage(message) {
var obj = JSON.parse(message);
// if the id is not in the tableStore, add it!
if (tableStore[obj.id] === undefined) {
// insert new row into table and save the index into `newTableRowIndex`
// store a blank value (updated below) and the index in the table where it will be displayed
tableStore[obj.id] = {value: undefined, tableIndex: newTableRowIndex};
}
// if the value recieved is different than the stored value, update the row
if (obj.value !== tableStore[obj.id].value) {
tableStore[obj.id].value = obj.value; // store the new value
var row = getTableRow(tableStore[obj.id].tableIndex); // your own function to get the row based on index
// update the row to display the new information
}
}
This could be improved and made to be more organized but you should get the idea.
This way it would only update anything in the display if the new information recieved is different than the old information already stored.
This way should also perform better than using an array would if the table has the potential to get very large as you would not need to search through the entire array every time to see if the id is already stored or not. You would simply access the tableStore entry directly using the id.
Related
I have jqGrid parent and another jqGrid child in a popup dialoag. The parent jqGrid has already a set of records in it and whenever I add new set of records from child jqGrid to parent jqGrid, I am losing the existing data from the parent jqGrid.
I tried the following.
concat the data from the parent to the child and then set it to the parent jqGrid.
var gridParent = jQuery("#parentGrid");
var existingRowsInParentGrid = gridParent.jqGrid('getGridParam','data');
var gridChild = jQuery("#childGrid");
var newRowsInChildGrid = gridChild .jqGrid('getGridParam','data');
var json = existingRowsInParentGrid.concat(newRowsInChildGrid );
//set the new concatnated data to parent
gridParent.jqGrid('setGridParam', {data: json}).trigger('reloadGrid');
Tried to use
Object.assign(existingRowsInParentGrid, newRowsInChildGrid)
Tried to use the extend feature.
var sum = jQuery.extend(existingRowsInParentGrid, newRowsInChildGrid );
It simply replaces the existing records with the new set of records. I am not adding records one at a time but setting the data in bulk. Does this make the difference?
I see lots of code, which tells me to add one record at a time. I was hoping that there will one way where we just need to add the whole set of new records at the end of the existing records.
Since you do not post the configuration of both grids it is difficult to help.
In most cases it is better to read the up to date documentation, rather than to read some posts. If you do not know addRowData can add multiple rows at once.
For more info refer the docs here (search for addRowData method)
Thanks, Tony.
I've found the solution after doing trial and error. Below is what I did which worked for me, perhaps it might help someone else!
Find the number of rows from the first jqGrid.
var existingRowsInParentGrid = gridParent.jqGrid('getGridParam','data');
var noOfRowsInParentGrid = existingRowsInParentGrid.length;
Find the number of rows from the second jqGrid.
var noOfRowsInChildGrid = gridChild.length;
Iterate through the 2nd grid elements and add rows to the parent grid at the correct position.
for(var i = 0;i < noOfRowsInChildGrid; i++) {
gridParent.jqGrid('addRowData', noOfRowsInParentGrid +1 , gridChild[i]);
noOfRowsInParentGrid = noOfRowsInParentGrid + 1;
}
My initial intention of loading the data in bulk still did not work though. But for now, I am happy as I will not have a huge amount of data in 2nd grid.
I have an Add-on I'm updating for Sheets. I want to store information each time the user runs one of two functions, so I've created a function to push than info to Document Properties. Ultimately I want to send that data to a sheet at my end once a certain number of values have been collected.
The problem I'm having is that when I run a test function to Log the data contained, it only shows me the most recent data; I'm not sure I'm adding to existing data or replacing it. The data pairs should include the user's email address and the name of the sheets tab created by one of two functions that call this storeStats function.
In short:
*Do I need to declare the name of the Property Store before adding data to it?
*How do I add more data without deleting the old?
*How can I check how much data is stored? I'm thinking along the lines of array.length, but not sure if that works in Properties
*I'm assuming I need to use the parse command to retrieve it and send to the sheet at my end. That may wind up in a separate question later, but any ideas are appreciated.
function storeStats(sheetTitle) {
var docProps = PropertiesService.getDocumentProperties();
var userID = Session.getActiveUser().getEmail();
var thisData = {user:userID, sheet:sheetTitle};
var thisDataStr = JSON.stringify(thisData);
var useData = "USEDATA"; //name of the properties store maybe
docProps.setProperties(useData,thisDataStr);
Logger.log(useData.length);
//send when enough values are present
//use parse to extract and send?
// /*if(/*see how many values are in the data set*/) {
//parse values from value set array
//send the whole batch to the range on the collection sheet
//} */
}
No errors are created thus far, but this only returns one email address and one sheet name rather than all values send from previous function calls.
docProps.setProperties(useData,thisDataStr); is not adding data to "USEDATA" if you want to add or append data to it you need to do something like this:
docprops.setProperty('USEDATA', docprops.getProperty('USEDATA') + thisDataStr);
Example:
function propertiesTest() {
var ps=PropertiesService.getScriptProperties();
ps.setProperty('test','');
for(var i=0;i<10;i++) {
ps.setProperty('test',ps.getProperty('test') + '\n' + i)
}
Logger.log(ps.getProperty('test'));
}
I want to import rows from one google sheet to the other, however source sheet imports a number of empty rows. Now I use a filter function to get rid of these rows but they will not disappear, can anyone tell me why?
var a = SpreadsheetApp.openByUrl("url").getSheetByName("Admin Use Only").getRange(4,1,6,21).getValues();
var b = SpreadsheetApp.getActive().getSheetByName('Credit_Detail');
b.getRange(b.getLastRow() +1, 1, a.length,21).setValues(a);
//filter function below:
var otarget=b.getRange(2,1,b.getLastRow()-1, 26).getValues();
var data=otarget.filter(function(r){
return !r.every(function(cell){
return cell === "";});
});
Logger.log(data);
b.getRange("A2:Z").clearContent();
b.getRange(3,1,data.length,data[0].length).setValues(data);
here's how I would do it. First, create an variable to store the array of the source. then run a for loop scanning the first column for empties. something like: for (var i = 0, i < data.length; i++) { if (data[i][0] != '') { XXXX } }
XXXX means that you can either put a code to create a new set of array which can be passed to the target sheet at once or use append row to transfer non blank rows to the target sheet one by one.
Note: Creating a new array to store non-empty rows would speedup the execution time if you are dealing with large data, thousands of rows.
What I'm looking for is an individual column searching function (exactly as this datatables spreadsheet example) for the Handsontable spreadsheet plugin.
What's already existing and has been developed by the Handsontable team is :
Multiple filtering Excel-like (but included in the PRO version) - Cons for my case are that it's not free, and it doesn't quite fit well what I'm looking for.
Highlighting the cell(s) or row(s) based on an user input - The Con is that I need to only display the relevant row(s)
Is there such thing as displaying only the relevant row(s) based on multiple inputs from an user with Handsontable ?
Based on the solution of this blog, I managed to code a solution.
See this JS fiddle that answers all my requirements.
The main function I was looking for is this one :
// The function push every row satisfying all the input values into an array that is loaded
function filter() {
var row, r_len, col, c_len;
var data = myData; // Keeping the integrity of the original data
var array = [];
var match = true;
for (row = 0, r_len = data.length; row < r_len; row++) {
for(col = 0, c_len = searchFields.length; col < c_len; col++) {
if(('' + data[row][col]).toLowerCase().indexOf(searchFields[col]) > -1);
else match=false;
}
if(match) array.push(data[row]);
match = true;
}
hot.loadData(array);
}
What I did is keeping synchronized a table of Strings with the input fields (searchFields), compare the data of each row between inputs and their corresponding column, and push into an array the relevant row(s) to finally display the resulting array. This function is called for any change in the input fields which result in a live table filtering.
Note that I tried my solution for ~10k rows and their isn't any performance issue with Chrome, Firefox and IE.
Also note that I managed to find a solution to keep synchronized the current displayed table with the original data when editing the values, but this is IMO out of the scope of this question. Please let me know in the comment if you're interested about this.
I'm trying to run the following code on a spreadsheet. The column of names is the 4th column. I'm attempting to run through that list of names, pushing each new unique name to an array (listOfNames), and then add a new worksheet (NamesList), and finally add the array to cell A1 of the new worksheet. When I run the code below, all I get is a blank popup with an OK and Cancel button. When I view the log, it is blank as well. I'm quite new at this, and I feel like I'm missing something obvious... just not sure what it is. Am I misunderstanding something specific to GAS rather than JS?
var sheet = SpreadsheetApp.getActiveSheet();
var listOfNames = new Array ();
function copyNames() {
var data = sheet.getDataRange().getValues();
for (i=0; i<=sheet.getLastRow(); i++){
var tempName = sheet.getDataRange(i,4).getValue();
for (i=0; i<=listOfNames.length; i++){
if (tempName != listOfNames[i]){
listOfNames.push(tempName);
logger.log(listOfNames);
}
}
}
sheet.insertSheet(ListOfEDs);
sheet.getRange('a1').setValue(listOfEDs);
}
Edit: I'm starting to see that this will push values multiple times to the list... so maybe it's just back to the drawing board all together. I have found other code that would create a list of unique elements, but wasn't really sure how that code worked. I thought I'd try to figure it out myself so I'd at least understand it.
EDIT 2: Ok... I tried some new code, but I'm still getting a blank message box, and nothing on the log. I wasn't sure if having i be the iterator for a for loop within a for loop was a bad thing, so I switched it to j. Also, I know there's the remove duplicates example, and have been looking at that, but am unsure why one needs to use join.
function copyNames() {
var sheet = SpreadsheetApp.getActiveSheet();
var listOfNames = new Array ();
var data = sheet.getDataRange().getValues();
for (i=2; i<=data.length; i++){ //starting at 2 because first row is a header
var tempName = data[i][4];
for (j=0; j<=listOfNames.length+1; j++){
if (tempName != listOfNames[j]){
listOfNames.push(tempName);
logger.log(listOfNames);
}
}
}
sheet.insertSheet("ListOfNames");
sheet.getRange('a1').setValue(listOfNames);
}
As far as I understand you went help instead of a ready solution, so here you go.
First:
Try to avoid using global variables. Keep all your variables inside of your functions. Otherwise you will have issues when you add more functions to your spreadsheet. Your Code should start like this:
function copyNames() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var listOfNames = new Array ();
Second:
Try to decrease the amount of service calls to spreadsheet. it takes several seconds for your script to go to your sheet and take the values. Instead of doing it each time, take these values once and push them in to a variable.
In your case, you already did that, however did not use that variable and kept using service calls. Check this line:
var data = sheet.getDataRange().getValues();
Now all of the data on that sheet is in a variable called data.
sheet.getDataRange(i,4).getValue();
is the same as
data[i][4];
The only difference is that in the first case it will take approximately 2 seconds to get that value, while in the second case only a few milliseconds.
Same goes for
sheet.getLastRow();
Either call it once and push it into a variable and use that, or in your case just use
data.length;
Third:
listOfNames is an empty array, so it's length is 0.
This line
for (i=0; i<=listOfNames.length; i++)
will not even run, as both i and listOfNames.length is 0.
That is why you logger does not give any output. Your script never get's to that line.
Fourth:
You do not have a variable called ListOfEDs, therefore your last two rows of code just give an error. Your script does not know what is ListOfEDs as it doesn't exist.
Hope this helps.