I have DB from where I'm getting my data of measurements. This is my code for looping through my measurements and adding them to OBX segments:
//getting MeasurementsData
var data = msg['measurements_measurement_data'].toString();
logger.info(data);
//inserting data in dictionary
var dictMeasurements = {};
for (var measurement in data) {
dictMeasurements[measurement] = data[measurement];
}
var dictSize = Object.keys(dictMeasurements).length;
var measureResult;
var measureType;
var counter = 1;
//Creating OBX measurements segments
while (counter <= dictSize) {
for (var key in dictMeasurements) {
//measure and result of measure
measureType = key;
measureResult = dictMeasurements[key];
createSegment('OBX', tmp, counter);
tmp['OBX'][counter]['OBX.1']['OBX.1.1'] = counter;
//ST-string or NM-numeric
tmp['OBX'][counter]['OBX.2']['OBX.2.1'] = isNumeric(measureResult);
tmp['OBX'][counter]['OBX.3']['OBX.3.1'] = ('ABI.' + measureType);
tmp['OBX'][counter]['OBX.3']['OBX.3.2'] = measureType;
tmp['OBX'][counter]['OBX.5']['OBX.5.1'] = measureResult;
tmp['OBX'][counter]['OBX.11']['OBX.11.1'] = 'F';
counter++;
}
}
If I run this code locally, it returns what I want (measurement type and its result), like:
cuffSizeArmLeft
1
cuffSizeArmRight
1
diaArmLeft
66
diaArmRight
66
But when I run it on MirthConnect chanel I get every letter from measure type in it's own column:
ST|ABI.0^0||{||||||F
OBX|2|ST|ABI.1^1||"||||||F
OBX|3|ST|ABI.2^2||c||||||F
OBX|4|ST|ABI.3^3||u||||||F
OBX|5|ST|ABI.4^4||f||||||F
OBX|6|ST|ABI.5^5||f||||||F
OBX|7|ST|ABI.6^6||S||||||F
OBX|8|ST|ABI.7^7||i||||||F
OBX|9|ST|ABI.8^8||z||||||F
OBX|10|ST|ABI.9^9||e||||||F
OBX|11|ST|ABI.10^10||A||||||F
OBX|12|ST|ABI.11^11||r||||||F
OBX|13|ST|ABI.12^12||m||||||F
OBX|14|ST|ABI.13^13||L||||||F
OBX|15|ST|ABI.14^14||e||||||F
OBX|16|ST|ABI.15^15||f||||||F
OBX|17|ST|ABI.16^16||t||||||F
OBX|18|ST|ABI.17^17||"||||||F
and my key in for loop is returning some numbers instead of measurement type, like it does locally.
This is similar question, but it didn't helped me.
Creating OBX segments from multiple DB rows
My another problem is that my function "isNumeric", is always returning a ST(string) instead a NM(numeric) type. Locally runs perfect.
function isNumeric(result) {
var reg = /^\d*\.?\d+$/;
if (typeof(result) == 'number') { return 'NM'; }
else { return 'ST'; }
}
console.log(isNumeric(140)); //return NM
console.log(isNumeric('test')); //return ST
Thanks for your help!
Related
Follow up to Sending duplicate data to array, then checking if said array is empty or not to decide what code to run next, if statement not working correctly.
I'm pretty much trying to copy the conditional formatting that I put to notify users of "bad" data, but with script to prevent sending bad data and giving unique error messages. With the answer from my last question, the checks and preventing sending no data or data with duplicates works, but I'm now stuck on preventing sending of data that does not match the format of a four digit number.
My conditional formatting was to set the background color to orange on anything that was not equal to or in between 1000 and 9999, but I'm having trouble getting the scripting to work--I'm trying to use negation of anything in between those two values as the "false" that will prompt an error message, and the "true" to let the rest of the script run that will send the data and notification emails out. However, this makes it say that there are bad values even if I do have the correct data in. Removing the negation lets anything go through, like it's not actually checking it. Any ideas?
The section I'm asking about is the last else if statement:
else if (!data.every(function(num) {return num >= 1000 && num <= 9999})) {
SpreadsheetApp.getUi().alert("You have incorrectly formatted tallies, tallies must be four digits.", SpreadsheetApp.getUi().ButtonSet.OK);
}
Total code is below.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssSheet = ss.getActiveSheet();
//https://techyesplease.com/code/google-apps-script-find-duplicates-sheets/
function readData() {
var dataColumn = 3;
var firstRow = 6;
var lastRow = ssSheet.getLastRow();
var numRows = lastRow - firstRow + 1;
var columnRange = ssSheet.getRange(firstRow, dataColumn, numRows);
//var rangeArray = columnRange.getValues();
// Convert to one dimensional array
//rangeArray = [].concat.apply([], rangeArray);
var rangeArray = columnRange.getValues().flat().filter(String);
return rangeArray;
}
// Sort data and find duplicates
function findDuplicates(dataAll) {
var sortedData = dataAll.slice().sort();
var duplicates = [];
for (var i = 0; i < sortedData.length - 1; i++) {
if (sortedData[i + 1] == sortedData[i]) {
duplicates.push(sortedData[i]);
}
}
return duplicates;
}
//Use the same string for this variable as the name for the requestor for his or her column of data. E.g., John Doe
//****GLOBALS****
var targetSheet = 'All Tallies'; //replace with sheet/tab name
var targetSpreadsheetID = 'id' //replace with destination ID
var targetURL = 'url'
//var dataNotificationReceivingEmailAddresses =
//Set up to be able to easily change what emails the data notification goes to?
function sendDataAndTimestamp2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssSheet = ss.getActiveSheet();
var sourceRange = ssSheet.getRange('C6:C401');
//assign the range you want to copy, could make C6:C? No, would like dynamic.
var data = sourceRange.getValues();
var nameRange = ssSheet.getRange('C4:D4');
var nameValue = nameRange.getDisplayValue();
var tallyDateRange = ssSheet.getRange('C2');
var tallyDateValue = tallyDateRange.getDisplayValue();
var tallyDateText = 'Tallies to run on '+ tallyDateValue;
var tallyAmountRange = ssSheet.getRange(8,1);
var tallyAmount = tallyAmountRange.getDisplayValue();
var tallyAmountNumberOnly = data.filter(String).length;
//Used as tallyAmount includes text, for some cases need the number only
var thisDocumentUrl = ss.getUrl();
//Variables for the sending/source spreadsheet above
//Initial confirmation alert, need checks for blank or error tallies first. First condition needs to loop through data variable and check that if any values are numbers not between 1000 and 9999, throw up Ui alert error message. Second condition goes to result variable?
//Reference the earlier functions
var dataArray = readData();
var duplicates = findDuplicates(dataArray);
//Need to check data and have error message if duplicates.length >=1, if 0 allow, refuse if data length less than 1
if (duplicates.length !== 0) {
SpreadsheetApp.getUi().alert("Your tallies include duplicates, please remove them then try again.", SpreadsheetApp.getUi().ButtonSet.OK);
Logger.log(duplicates);
}
else if (dataArray.length ===0) {
SpreadsheetApp.getUi().alert("You have not input any tallies.", SpreadsheetApp.getUi().ButtonSet.OK);
}
else if (!data.every(function(num) {return num >= 1000 && num <= 9999})) {
SpreadsheetApp.getUi().alert("You have incorrectly formatted tallies, tallies must be four digits.", SpreadsheetApp.getUi().ButtonSet.OK);
}
/*https://www.youtube.com/watch?v=gaC290XzPX4&list=PLv9Pf9aNgemvD9NFa86_udt-NWh37efmD&index=10
Every method
var arr = [1,2,3,4];
var allTalliesGood = data.every(function(num){
return num < 9
});
//Use with num >= 1000, num <=9999, and then if true continue, if false stop and give error msg
*/
/*
dataFiltered = data.filter(filterlogic);
var filterlogic = function(tally){
if (tally >= 1000 && tally <= 9999){
return true;
} else {
return false
}
}
//if use false for good tallies, and rename dataFiltered to something like "dataBad", then check if dataBad has true matches in it...
//need to check for strings? what happens if letters/symbols in tally range?
//var dataBad = data.filter(function(tally){ return tally < 1000 || tally > 9999;});
// use OR (||) for getting tallies great than or less than, what about letters/symbols? Use NOT good range?
//https://www.youtube.com/watch?v=hPCIOohF0Fg&list=PLv9Pf9aNgemvD9NFa86_udt-NWh37efmD&index=8
//sort method link above
*/
else {
// rest of code
var result = SpreadsheetApp.getUi().alert("You're about to notify scheduler of the following number of tallies: " + tallyAmountNumberOnly, SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
if(result === SpreadsheetApp.getUi().Button.OK) {
//Code to send out emails and data
As far as I can tell in the function sendDataAndTimestamp2() you need to get rid of empty values from data. It can be done with filter(String) method:
var data = sourceRange.getValues().filter(String); // <-- here
. . .
else if (!data.every(function(num) {return num >= 1000 && num <= 9999}))
. . .
Or, I don't know, perhaps you meant dataArray instead of data:
else if (!dataArray.every(function(num) {return num >= 1000 && num <= 9999}))
Btw, the line can be shortened a bit:
else if (!data.every(x => (x >= 1000) && (x <= 9999)))
I currently have a list with two columns. The first column is student name, and the second column is the number of points they have.
I imported this list from multiple spreadsheets so there were many duplicates on the names of the students. I am able to remove the duplicates, but I want to keep a tally on the total points they have. For example:
Amy 10
Bob 9
Carol 15
Amy 12
would turn into:
Amy 22
Bob 9
Carol 15
This is what I have so far:
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var data = sheet.getRange("A2:B1000").getValues();
var newData = new Array();
var k = 0
var finallist = []
for(i in data){
k++;
var row = data[i];
var duplicate = false;
for(j in newData){
if(row[0] == newData[j][0]){
duplicate = true;
var storedHour = sheet.getRange("B"+k).getValue();
var position = finallist.indexOf(row[0]);
var originalCell = sheet.getRange("B"+(position+1));
var originalHour = originalCell.getValue();
originalCell.setValue(originalHour + storedHour);
sheet.getRange(k,2).setValue("")
sheet.getRange(k,1).setValue("")
}
}
if(!duplicate){
newData.push(row);
finallist.push(row[0])
}
}
}
The problem I'm having is that we have a really large data sample and I'm afraid it may run over Google's 5 minute maximum execution time. Is there another more efficient way to achieve my goal?
Your code is running slow because Spreadsheets API methods (like getRange) are time consuming and much slower then other JavaScript code.
Here is optimized function with reduced number of such Spreadsheets API calls:
function calcNumbers()
{
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var lastRow = sheet.getLastRow();
var dataRange = sheet.getRange(2, 1, lastRow-1, 2);
var data = dataRange.getValues();
var pointsByName = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var curName = row[0];
var curNumber = row[1];
// empty name
if (!curName.trim())
{
continue;
}
// if name found first time, save it to object
if (!pointsByName[curName])
{
pointsByName[curName] = Number(curNumber);
}
// if duplicate, sum numbers
else
{
pointsByName[curName] += curNumber;
}
}
// prepare data for output
var outputData = Object.keys(pointsByName).map(function(name){
return [name, pointsByName[name]];
});
// clear old data
dataRange.clearContent();
// write calculated data
var newDataRange = sheet.getRange(2, 1, outputData.length, 2);
newDataRange.setValues(outputData);
}
Sorting before comparing allows looking at the next item only instead of all items for each iteration. A spillover benefit is finallist result is alphabatized. Execution time reduction significant.
function sumDups() {
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var data = sheet.getRange("A2:B" + sheet.getLastRow()).getValues().sort();
var finallist = [];
for(var i = 0; i<= data.length - 1; i++){
var hours = data[i][1];
while((i < data.length - 1) && (data[i][0] == data[i+1][0])) {
hours += data[i+1][1];
i++;
};
finallist.push([data[i][0], hours]);
};
Logger.log(finallist);
}
Edit: the simple data structure with the name being in the first column allows this to work. For anything more complex understanding and applying the methods shown in #Kos's answer is preferable
Currently working on an application which requires to display a set of values in different currencies. This is part of an application but everything I provide here should be enough as this is the main section I am working with. I have a json file which is read in and it is stored into an array called valuesArray, this array has all the information, such as the amount, currency, etc. With the currencies being sorted with highest first to the lowest on display like this:
EUR 500.00
USD 200.00
This is the code that I have created but it seems like this wouldn't be effective the more currencies I have. I've just put an array declaration above the function but just above this function is where I do all the json stuff and adding it into the array. $scope.valuesArray has data at this point.
$scope.valuesArray =[];
$scope.total = function()
{
var eur_total = 0;
var usd_total = 0;
if (typeof $scope.valuesArray != 'undefined')
{
var length = $scope.valuesArray.length;
for (var i = 0; i<length ; i++)
{
switch($scope.valuesArray[i].currency)
{
case "USD":
usd_total += parseFloat($scope.valuesArray[i].value);
break;
default:
eur_total += parseFloat($scope.valuesArray[i].value);
break;
}
}
}
var cost_total= [usd_total,eur_total];
total.sort(function(a, b){return b-a});
return format_to_decimal(total[0]) + "\x0A" + format_to_decimal(total[1]);
}
In my for loop I go through every single data in the array and break each currency down within the switch statement and finding the total amount of each currencies.
The last bit is kind of temporary as I couldn't figure out a different way of how to do it. I sort the totals for the currencies I have from the highest at the top.
I return the function with a function call for format_numeric_with_commas which gives me the value in proper currency format and this displays the value. Will update this and add that code when I get to it. But I have used the indexes as a rough logic to show what I want to get out of it. So in this case, total[0] should be 500.00 and total[1] should be 200.00.
On top of this I want to be able to display the currency type for each. So like the example above.
You can try to save all the calculations in the array with currency index.
$scope.valuesArray = [];
$scope.total = function () {
var totalsArray = [];
if (typeof $scope.valuesArray != 'undefined') {
var length = $scope.valuesArray.length
for (var i = 0; i < length; i++) {
if (!totalsArray[$scope.valuesArray[i].currency]) {
totalsArray[$scope.valuesArray[i].currency] = 0;
}
totalsArray[$scope.valuesArray[i].currency] += $scope.valuesArray[i].value;
}
}
var cost_total = [];
for (var k in totalsArray) {
cost_total.push(currency:k,value:totalsArray[k]);
}
cost_total.sort(function (a, b) {
return b.value - a.value
});
return format_to_decimal(cost_total[0].value)+cost_total[0].currency + "\x0A" + format_to_decimal(cost_total[1].value);
I'm having trouble passing a WCHAR_T to ReadProcessMemory
This is how to succesfully pass a pointers address to ReadProcessMemory, I can do it with structures:
remote_tbb = ralloc_alloc(struct_TBButton.size);
var rez = SendMessage(hToolbar, TB_GETBUTTON, i, ctypes.voidptr_t(remote_tbb));
if (!rez) { throw new Error('Failed on SendMessage of TB_GETBUTTON') }
var local_tbb = new struct_TBButton();
var retRead = ralloc_read(remote_tbb, local_tbb.address());
var freed = ralloc_free(remote_tbb);
But now I need to do with WCHAR_T, so this is what I have:
var chars = SendMessage(hToolbar, TB_GETBUTTONTEXTW, local_tbb.idCommand, ctypes.voidptr_t(0));
console.log('chars=', chars, chars.toString(), uneval(chars));
if (chars && parseInt(chars.toString()) > 0) {
var remote_buf = ralloc_alloc(parseInt(chars.toString()));
var charsRe = SendMessage(hToolbar, TB_GETBUTTONTEXTW, local_tbb.idCommand, ctypes.voidptr_t(remote_buf));
console.log('charsRe=', charsRe);
var local_buf = ctypes.jschar; //WCHAR_T
var retRead = ralloc_read(remote_buf, local_buf.address()); ///PROBLEM LINE
console.log('retRead=', retRead);
var freed = ralloc_free(remote_buf);
console.log('freed=', freed);
console.log('Button Text = ', local_buf, local_buf.toString());
} else {
console.log('Button Text = NONE');
}
So my problem is on line:
var retRead = ralloc_read(remote_buf, local_buf.address());`
and it is specifically on the local_buf.address()
Errors in my experimenting that get thrown are:
expected type pointer, got ctypes.jschar
local_buf.address is not a function
So how to pass WCHAR_T as reference?
Edit:
Here is my ralloc_read implemetnation:
function ralloc_read(remote_address, local_buffer) {
var found_addr;
for (var i = 0; i < buffers.length; i++) {
if (buffers[i][0] == remote_address) {
found_addr = buffers[i]
break;
}
}
if (!found_addr) {
return null;
}
/*using the found remote address(found_addr[0]),
*i read size bytes (found_addr[1]) into my local_buffer*/
//console.info('found_addr[0]', found_addr[0].toString());
var rez = ReadProcessMemory(proc, found_addr[0], local_buffer, found_addr[1], 0);
return rez;
}
If ralloc_read calls ReadProcessMemory, then you'll need to allocate a jschar array that will receive the result.
var local_buf = ctypes.jschar.array()(chars);
ralloc_read(remote_buf, local_buf.address());
var str = local_buf.readString();
Edit However, the allocation call is wrong:
ralloc_alloc(parseInt(chars.toString()));
This will allocate chars bytes, e.g. chars = 11, 11 bytes.
A wchar_t/jschar however is not 1 byte but 2 bytes.
ctypes.jschar.size
// 2
So you'll actually need to allocate a remote memory buffer that is larger:
ralloc_alloc(parseInt(chars.toString()) * ctypes.jschar.size);
// That would be ralloc_alloc(count * sizeof(wchar_t*)) in C/C++
The local_buf stuff is correct, though as js-ctypes arrays will automatically calculate the required storage if it knows the size of the array element type, so a ctypes.jschar.array()(11) buffer will actually have 11 elements of size 2 bytes, i.e. 11 items * 2 bytes/item == 22 bytes.
How can I obtain driving length between two stops in ArcGIS? I'm placing route from RouteTask service on a map and want to get lengths from that response too. I've thought about doing some iteration in DirectionsFeatureSet, but I already see that what I'm doing is complete nonsense.
var directions = solveResult[0].directions;
console.log(directions);
var length = 0;
var location = 0;
var obj = {};
$.each(directions.features, function (ind, val) {
var txt = val.attributes.text;
var indexOfLocation = txt.indexOf('Location');
if (indexOfLocation != -1) {
var digitTransform = txt.substring(indexOfLocation + 9);
var digit = "";
for (var i = 0; i < digitTransform.length; i++) {
var char = digitTransform[i];
if (isNumber(char)) {
digit += char;
} else break;
}
}
});
That's what I already did and that makes no sense.
In Google Maps API it's clear - every leg has it's own length. In ArcGIS responses I see no such easy approach.
The length is available as an attribute of each returned feature. So, given your directions variable, the following would give you the length of the first leg of the route:
directions.features[0].attributes.length