Google Visualization - StringFilter - Searching w. OR - search

I have stringfilters bind to my table of data.
What i would like to get from the stringfilter is to be able to search like you would do with queries.
There is a colomn with names - a name for each row - for example - "Steve","Monica","Andreas","Michael","Steve","Andreas",...
I want to have both rows with Monica and Steve from the StringFilter.
I would like to be able to search like this
Steve+Monica
or
"Steve"+"Monica"
or
"Steve","Monica"
This is one of my stringfilters:
var stringFilter1 = new google.visualization.ControlWrapper({
controlType: 'StringFilter',
containerId: 'string_filter_div_1',
options: {
filterColumnIndex: 0, matchType : 'any'
}
});

I had a similar problem, and I ended up creating my own function for filtering my rows. I made an example with the function that you describe, but I'm not sure it's the best or the right way, but it works.
One flaw is that you need to type in the names exactly as they are entered, the whole name and with capital letters.
Fiddle, try to add multiple names separated with a "+" (and no spaces).
The function I added looks like this:
function redrawChart(filterString) {
var filterWords = filterString.split("+")
var rows = []
for(i = 0; i < filterWords.length; i++) {
rows = rows.concat(data.getFilteredRows([{value:filterWords[i], column:0}]))
}
return rows
}
And the listener that listens for updates in your string input looks like:
google.visualization.events.addListener(control, 'statechange', function () {
if (control.getState().value == '') {
realChart.setView({'rows': null})
}else{
realChart.setView({'rows': redrawChart(control.getState().value)})
}
realChart.draw();
});
Probably not a complete solution, but maybe some new ideas and directions to your own thoughts.

Related

Copy Excel cell value and add rows to another table

In a table (in excel) in a column I have some number(A).
I want the flow to take that number (A) and to create number of rows equels to Number (A)
For example if number(A) is 4, then in another table to be added 4 rows
I've made an assumption on the source and destination tables. This concept can be adjusted and applied to suit your own scenario.
I'd be using Office Scripts to do this. If you've never used it then feel free to consult the Microsoft documentation to get you going ...
https://learn.microsoft.com/en-us/office/dev/scripts/tutorials/excel-tutorial
This is the script you need to create (change the name of your tables accordingly) ...
function main(workbook: ExcelScript.Workbook)
{
var addRowsTable = workbook.getTable('TableRowsToAdd');
var addRowsToTable = workbook.getTable('TableAddRowsToTable');
var addRowsTableDataRange = addRowsTable.getRangeBetweenHeaderAndTotal();
var addRowsTableDataRangeValues = addRowsTableDataRange.getValues();
// Sum the values so we can determine how many more rows need to be added
// to the destination table.
var sumOfAllRowsToBeInExistence = 0;
for (var i = 0; i < addRowsTableDataRangeValues.length; i++) {
if (!isNaN(addRowsTableDataRangeValues[i][0])) {
sumOfAllRowsToBeInExistence += Number(addRowsTableDataRangeValues[i][0]);
}
}
var currentRowCount = addRowsToTable.getRangeBetweenHeaderAndTotal().getRowCount();
var rowsToAdd = sumOfAllRowsToBeInExistence - currentRowCount;
console.log(`Current row count = ${currentRowCount}`);
console.log(`Rows to add = ${rowsToAdd}`);
if (rowsToAdd > 0) {
/*
The approach below is contentious given the performance impact but this approach ...
for (var i = 1; i <= rowsToAdd; i++) {
... didn't always yield the correct result. May be a bug but needs investigation.
Ultimately, there are a few ways to achieve the same result, like using the resize method.
This was the easiest option for a StackOverflow answer.
*/
while (addRowsToTable.getRangeBetweenHeaderAndTotal().getRowCount() <
sumOfAllRowsToBeInExistence) {
addRowsToTable.addRows();
}
}
}
You can then call that from PowerAutomate using the Run script action under Excel Online (Business) ...
You can use that approach or all of the actions that are available in PowerAutomate which will achieve the same sort of thing.
IMO, Using Office Scripts is much easier. Creating a large flow can be a real pain in the backside to deal with given there'll be a whole heap of actions that you'll need to throw in to reach the same outcome.
I would pass the number of rows to add in an office scripts script as a parameter. Once you have the value, create a JSON string of a 2d array. You want to create a loop using the number of rows to add. In the loop you continue to concatenate the 2d array. Once you've exited the loop, parse the JSON string and add the 2d array to the table. You can see how you code might look below:
function main(workbook: ExcelScript.Workbook, rowsToAdd: number)
{
//set table name
let tbl = workbook.getTable("table2")
//initialize json string with open bracket
let jsonArrString = "["
//set the temp json string with a 2d array
let tempJsonArr = '["",""],'
//concatenate json string equal to the number of rows to add
for (let i = 0; i < rowsToAdd; i++){
jsonArrString += tempJsonArr
}
//remove extra comma from JSON string
jsonArrString = jsonArrString.slice(0, jsonArrString.length-1)
//add closing bracket to JSON string
jsonArrString += "]"
//parse json string into array
let jsonArr: string[][] = JSON.parse(jsonArrString)
//add array to table to add the number of rows
tbl.addRows(null,jsonArr)
}

Populate Suitelet Sublist from a Saved Search with Formulas in the Search

#bknights posted an good answer to another question around populating a sublist in a suitelet.
However, my question follows on from that when using bk's code:
function getJoinedName(col) {
var join = col.getJoin();
return join ? col.getName() + '__' + join : col.getName();
}
searchResults[0].getAllColumns().forEach(function(col) {
sublist.addField(getJoinedName(col), 'text', col.getLabel());
nlapiLogExecution('DEBUG', 'Column Label', col.getLabel());
});
var resolvedJoins = searchResults.map(function(sr) {
var ret = {
id: sr.getId()
};
sr.getAllColumns().forEach(function(col) {
ret[getJoinedName(col)] = sr.getText(col) || sr.getValue(col);
});
return ret;
});
sublist.setLineItemValues(resolvedJoins);
The above works with a standard search with no formulae... How can we do this when I have multiple search columns which are formulae?
Using API1.0
In your search definition add a label to all formula columns. Then your column keys can be derived like:
function getJoinedName(col) {
if(col.getName().indexOf('formula') === 0 && col.getLabel()){
return 'lbl_'+ col.getLabel().toLowerCase();
}
var join = col.getJoin();
return join ? col.getName() + '__' + join : col.getName();
}
You can just get all the columns of the search result. columns = result[0].getColumns(). The reference the column where the formula column is. So if you look in the UI and it is the third from the top, you can get the value using result[0].getValue(columns[2])
This solution is dependent on the order of rows not changing.
Also if your saved search has labels for the Formulas, you can just use the labels as the field id.

Update a row in google sheets based on duplicate

I'm designing a script that takes an object (jsonData[data]) and inputs its values into a different sheet based on which product it is.
Currently the script inputs all the data into a new row each time the form reaches a new stage, however the form goes through 4 stages of approval and so I'm finding each submission being entered into 4 different rows. Each submission has an "Id" value within the object which remains the same (but each submission could also be on any row in the sheet as it's used a lot).
I'm checking whether the ID exists in the sheet and using iteration to find the row number:
function updatePlatformBulkInfo(jsonData) {
var sheetUrl = "https://docs.google.com/spreadsheets/d/13U9r9Lu2Fq1WTT8pQ128heCm6_gMmH1R4O6u8e7kvBo/edit#gid=0";
var sheetName = "PlatformBulkSetup";
var doc = SpreadsheetApp.openByUrl(sheetUrl);
var sheet = doc.getSheetByName(sheetName);
var rowList = [];
var formId = jsonData["Id"];
var allSheetData = sheet.getDataRange().getValues();
setLog("AllSheetData = " + allSheetData[1][11]) //Logs to ensure data is collected correctly
var rowEdited = false;
var rowNumber = 0;
//Check whether ID exists in the sheet
for (var i = 0; i < allSheetData.length; i++) {
if(allSheetData[i][11] == formId) {
rowEdited = true;
} else {
rowNumber += 1;
}
}
My issue is with the next part:
//Append row if ID isn't duplicate or update row if duplicate found
if (rowEdited == false) {
for (var data in jsonData) {
rowList.push(jsonData[data])
}
setLog("***Row List = " + rowList + " ***");
setLog("***Current Row Number = " + rowNumber + " ***");
sheet.appendRow(rowList);
} else if(rowEdited == true){
var newRowValue = jsonData[data];
sheet.getRange(rowNumber, 1).setValues(newRowValue);
}
Everything works fine if the duplicate isn't found (the objects values are appended to the sheet). But if a duplicate is found I'm getting the error:
Cannot find method setValues(string)
This looks to me like i'm passing a string instead of an object, but as far as I'm aware I've already converted the JSON string into an object:
var jsonString = e.postData.getDataAsString();
var jsonData = JSON.parse(jsonString);
How can I modify my script to write the updated data to the matched row?
It's unclear based on your code whether or not you will actually write to the correct cell in the case of a duplicate. As presented, it looks as though you loop over the sheet data, incrementing a row number if the duplicate is not found. Then, after completing the loop, you write to the sheet, in the row described by rowNumber, even though your code as written changes rowNumber after finding a duplicate.
To address this, your loop needs to exit upon finding a duplicate:
var duplicateRow = null, checkedCol = /* your column to check */;
for(var r = 0, rows = allSheetData.length; r < rows; ++r) {
if(allSheetData[r][checkedCol] === formId) {
// Convert from 0-base Javascript index to 1-base Range index.
duplicateRow = ++r;
// Stop iterating through allSheetData, since we found the row.
break;
}
}
In both cases (append vs modify), you seem to want the same output. Rather than write the code to build the output twice, do it outside the loop. Note that the order of enumeration specified by the for ... in ... pattern is not dependable, so if you need the elements to appear in a certain order in the output, you should explicitly place them in their desired order.
If a duplicate ID situation is supposed to write different data in different cells, then the following two snippets will need to be adapted to suit. The general idea and instructions still apply.
var dataToWrite = [];
/* add items to `dataToWrite`, making an Object[] */
Then, to determine whether to append or modify, test if duplicateRow is null:
if(dataToWrite.length) {
if(duplicateRow === null) {
sheet.appendRow(dataToWrite);
} else {
// Overwriting a row. Select as many columns as we have data to write.
var toEdit = sheet.getRange(duplicateRow, 1, 1, dataToWrite.length);
// Because setValues requires an Object[][], wrap `dataToWrite` in an array.
// This creates a 1 row x N column array. If the range to overwrite was not a
// single row, a different approach would be needed.
toEdit.setValues( [dataToWrite] );
}
}
Below is the most basic solution. At the end of this post, I'll expand on how this can be improved. I don't know how your data is organized, how exactly you generate new unique ids for your records, etc., but let's assume it looks something like this.
Suppose we need to update the existing record with new data. I assume your JSON contains key-value pairs for each field:
var chris = {
id:2,
name: "Chris",
age: 29,
city: "Amsterdam"
};
Updating a record breaks down into several steps:
1) Creating a row array from your object. Note that the setValues() method accepts a 2D array as an argument, while the appendRow() method of the Sheet class accepts a single-dimension array.
2) Finding the matching id in your table if it exists. The 'for' loop is not very well-suited for this idea unless you put 'break' after the matching id value is found. Otherwise, it will loop over the entire array of values, which is redundant. Similarly, there's no need to retrieve the entire data range as the only thing you need is the "id" column.
IMPORTANT: to get the row number, you must increment the array index of the matching value by 1 as array indices start from 0. Also, if your spreadsheet contains 1 or more header rows (mine does), you must also factor in the offset and increment the value by the number of headers.
3) Based on the matching row number, build the range object for that row and update values. If no matching row is found, call appendRow() method of the Sheet class.
function updateRecord(query) {
rowData = [];
var keys = Object.keys(query);
keys.forEach(function(key){
rowData.push(query[key]);
})
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheets()[0];
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var idColumn = 1;
var ids = sheet.getRange(2, idColumn, sheet.getLastRow() - 1, 1).getValues();
var i = 0;
var matchedRow;
do {
if (ids[i] == query.id) { matchedRow = i + 2; }
i++;
} while (!matchedRow && i < ids.length);
if (matchedRow) {
var row = sheet.getRange(matchedRow, idColumn, 1, rowData.length);
row.setValues([rowData]);
} else {
sheet.appendRow(rowData);
}
}
NOTE: if your query contains only some fields that need to be updated (say, the 'id' and the 'name' field), the corresponding columns for these fields will be
headers.indexOf(query[key]) + 1;
Possible improvements
If the goal is to use the spreadsheet as a database and define all CRUD (Create, Read, Write, Delete) operations. While the exact steps are beyond the scope of the answer, here's the gist of it.
1) Deploy and publish the spreadsheet-bound script as a web app, with the access set to "anyone, even anonymous".
function doGet(e) {
handleResponse(e);
}
function doPost(e) {
handleRespone(e);
}
function handleResponse(e) {
if (e.contentLength == -1) {
//handle GET request
} else {
//handle POST request
}
}
2) Define the structure of your queries. For example, getting the list of values and finding a value by id can be done via GET requests and passing parameters in the url. Queries that add, remove, or modify data can be sent as payload via POST request. GAS doesn't support other methods besides GET and POST, but you can simulate this by including relevant methods in the body of your query and then selecting corresponding actions inside handleResponse() function.
3) Make requests to the spreadsheet URL via UrlFetchApp. More details on web apps https://developers.google.com/apps-script/guides/web

How to Remove the addPreSearch Filter

I am trying to remove the PreSearch filer and my code is as below. How can I achieve the same?
Xrm.Page.getControl("productid").removePreSearch(function () {
Object
});
Xrm.Page.getControl("productid").addPreSearch(function () {
fetchxml2();
});
function fetchxml2() {
var fetchXml1 = "<filter type='and'>"
fetchXml1 += "<condition attribute='productid' operator='in' >";
for (var i = 0; i < Itemid.length; i++) {
fetchXml1 += "<value>" + Itemid[i] + "</value>";
}
fetchXml1 += "</condition>";
fetchXml1 += "</filter>";
Xrm.Page.getControl("productid").addCustomFilter(fetchXml1);
//Xrm.Page.getControl("productid").removePreSearch(fetchXml1);
};
In order to be able to remove the handler via removePreSearch, avoid using an anonymous function by creating a named function and using that in both addPreSearch and removePreSearch:
function preSearchHandler(){
fetchxml2();
}
Xrm.Page.getControl("productid").removePreSearch(preSearchHandler);
Xrm.Page.getControl("productid").addPreSearch(preSearchHandler);
Just wanted to add this to the discussion:
If you, say, have three different custom filters on a lookup field, the functionality will stack when you apply a new filter.
For example, if you have an option set that calls addPreSearch() on the field, if you select all three different options, you will have all three filters applied to the field simultaneously.
say the option set has three options of [option A, option B, option C],
the corresponding functions are, for simplicity [filterA, filterB, filterC],
on the change event of the option set, for each filter that you apply, simply remove the other two (in this case).
if (optionSet == 810500000) {//option A
Xrm.Page.getControl('lookup').addPreSearch(filterA);
Xrm.Page.getControl('lookup').removePreSearch(filterB);
Xrm.Page.getControl('lookup').removePreSearch(filterC);
}
else if (optionSet == 810500001) {//option B
Xrm.Page.getControl('lookup').addPreSearch(filterB);
Xrm.Page.getControl('lookup').removePreSearch(filterA);
Xrm.Page.getControl('lookup').removePreSearch(filterC);
}//so on and so forth
I hope this helps someone out, I was able to apply custom filters to a lookup based on four distinct selections and remove the "stackable" filters by addition and removal in this manner. It's a little ugly, but, hey, it works. At the end of the day, sometimes the most elegant solution is to just win, win win win win.
If you need more context (fetchXml) and such, I can post that, too...but it doesn't really go along with the point I was trying to make. These filters can be applied simultaneously! That's the main idea I wanted to convey here.

YUI Column Selection

I'm having issues using YUI's DataTable Column Selection Functionality. I've tried,
myEndColDataTable.subscribe("theadCellClickEvent", myEndColDataTable.onEventSelectColumn);
and
myEndColDataTable.subscribe("cellClickEvent", function (oArgs) {
this.selectColumn(this.getColumn(oArgs.target));
});
The issue is, I have an initial column selected programmatically. I can highlight the other column, but it doesn't remove the selection from the initially-selected column.
You are correct - there is no quick clean solution.
YUI DataTable currently (as of 2.8) lacks an unselectAllColmns method to match unselectAllRows (which is called by onEventSelectRow).
It is also worth noting that onEventSelectColumn selects the column header, so unselectAllCells will not work.
You could implement your own unselectAllColumns() function like this:
function unselectAllColumns (dataTable) {
var i, oColumn, oColumnSet = dataTable.getColumnSet();
for (i=0; i<oColumnSet.keys.length; i++) {
oColumn = oColumnSet.keys[i];
if (oColumn.selected) {
dataTable.unselectColumn(oColumn);
}
}
}
This will be marginally more efficient than using getSelectedColumns() because you will not need to build an intermediate array of only selected columns (looking at the source getSelectedColumns calls getColumnSet and walks the array just as above).
I guess I can do this, but its not elegant. There has to be a better way.
myEndColDataTable.subscribe("cellClickEvent", function (oArgs) {
var colUnSelect = myEndColDataTable.getSelectedColumns();
myEndColDataTable.unselectColumn(colUnSelect[0]);
myEndColDataTable.selectColumn(myEndColDataTable.getColumn(oArgs.target));
});

Resources