I'll keep this very simple. Why does this work:
var heights = arrayOf(1,2,3,4,2,3,4)
var sortedHeights = heights.copyOf()
sortedHeights.sort()
for (i in 0..heights.size-1)
{
println(sortedHeights[i])
}
But this does not?
var heights = arrayOf(1,2,3,4,2,3,4)
var sortedHeights = heights.copyOf().sort()
for (i in 0..heights.size-1)
{
println(sortedHeights[i])
}
As you can see the only difference is the chaining with the sortedHeights array. If copyOf() returns an array, shouldn't I be able to chain it with sort()?
forpas answered why your code doesn't work in the comments, but you can use scope functions to allow chaining anyway:
var sortedHeights = heights.copyOf().apply { sort() }
or
var sortedHeights = heights.copyOf().also { it.sort() }
There is a builtin function performs both a copyOf() and a sorting: sortedArray()
You can change your line of code to:
var sortedHeights = heights.sortedArray()
(but you should really be using val instead of var unless you intend to change the value of the variable)
Related
I'm updating a web app from using Knockout.js Templates to Vue 3 Components. This issue is not in the components but in the JS where I'm creating objects to be used in the components.
I'm running into strange behaviour around using refs where an object member is not always being set as a ref. I haven't been able to recreate this in an example but a simplified version of the structure is that we have an attribute that returns an object that contains the ref value.
function Attribute(val) {
var value = ref(val);
return {
value: value
}
}
We have a builder that is used to add labels and other items such permissions and classes to the UI.
function UIBuilder() {
var self = this;
var newElement = {};
function newText(value) {
newElement.inputType = 'TEXT';
newElement.value = value;
return self;
}
function addLabel(lbl) {
newElement.label = lbl;
return self;
}
function build() {
return newElement;
}
this.newText = newText;
this.addLabel = addLabel;
this.build = build;
}
Finally there is a function that returns an object that contains everything that the component needs.
function TextInput(initValue) {
var self = this;
self.label = initValue.label;
self.value = initValue.value;
var textInput = {
label: self.label,
value: self.value
};
return textInput;
}
Then I create an object to be passed to the component.
var attr = new Attribute(5225);
var textBox = new TextInput(new UIBuilder().newText(attr).addLabel('Description').build());
This structure works using Knockout and I'm trying to keep as much of the existing code as possible.
Most of the time this works but there are some occasions where the value is coming in as a string rather then a ref. I haven't been able to isolate why this is happening. While debugging most of the time the values for initValue.value and self.value look like this in the watch on VS Code.
On some occasions it changes to string values even though the objects were created using the same functions.
I checked the initValue object when taking the screenshot and it appeared to be a ref
As far as I can see the value should stay as a ref. I'm not unwrapping it in code. Why is this happening? What should I look for?
I would like to store an entire sentence an user said and store it.
This his how I did but I can't get the sentence from A to Z an an whole entity, just few parts knows as "number", "location", ....
merge(request) {
return new Promise(function(resolve, reject) {
var entities = request.entities;
var context = request.context;
var message = request.message;
var sessionId = request.sessionId;
var intent = firstEntityValue(entities, 'intent');
if (intent == "write_free_text") {
context.free_text = request["text"];
}
if (intent == "choose_city") {
var city = firstEntityValue(entities, 'location');
context.city = city;
}
return resolve(context);
});
}
How can I do that and store the whole sentence with merge function ? Thank you
If you want the whole sentence, maybe you don't needs a entity, just get the message sent:
// Merge action
function merge(request) {
context.freetext = request["text"];
return context;
}
Bot: https://wit.ai/Godoy/bottest/stories/4da2840f-513e-42ed-a494-c5516c07242e
Fiddle with code: https://wit-ai.github.io/witty-fiddle/?id=e4c16a624c87d37f9c0c29d8299ca5fc
if you want to get the whole phrase, use the wit/phrase_to_translate built-in entity
a snapshot of the uunderstanding tab
you just have to train the bot, once or twice.
It will pick up all the free text later.
Another GAS question. I read the documentation on ScriptProperties and bulk-setting properties, as well as (again) the documentation on best practices. However, I'm not familiar with Javascript, and still new to GAS, and I keep running over the rate limit for API calls as a result.
Basically, I would like to 'get' all these properties from the Spreadsheet, put them in an Array or Object or something, then bulk set all of them. I have the keys and values correct, I just don't know how to temp store them in a JS Object or Array or some data type that will be accepted by the setProperties(Object) method.
Here is the current code (horrendous Sleep timer is the only thing that is working...):
function setSubsequentProperties(propertyRange, sheet) {
// Get the other Library Properties based on the new Range values.
// propertyRange is the 2-column 1-row Range for the key/value pairs
var tableRange = ScriptProperties.getProperty(propertyRange);
var dataRange = sheet.getRange(tableRange);
var data = dataRange.getValues();
// Create a 'properties' Object for bulk-setting to prevent overusing API calls
//var properties = new Array(data.length);
// Set a new Script Property for each row
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var propertyKey = row[0];
var propertyValue = row[1];
// var myObject = allMyPropertiesInOneObject
ScriptProperties.setProperty(propertyKey, propertyValue);
Utilities.sleep(1000);
}
// setProperties(myObject);
}
How might I go about adding propertyKey and propertyValue to an Object to bulk set them all at once? The pseudo-idea is commented out in the code block.
Since you say you are new to Javascript, I assume you are new to JSON as well. There is indeed a ScriptProperties.setProperties() method which takes a JSON object as argument.
Modifying just the setProperty() bit of your code, here is what you can do
function setSubsequentProperties(propertyRange, sheet) {
// Get the other Library Properties based on the new Range values.
// propertyRange is the 2-column 1-row Range for the key/value pairs
var tableRange = ScriptProperties.getProperty(propertyRange);
var dataRange = sheet.getRange(tableRange);
var data = dataRange.getValues();
// Create a 'properties' Object for bulk-setting to prevent overusing API calls
//var properties = new Array(data.length);
// Set a new Script Property for each row
var myObject = {} ;
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var propertyKey = row[0];
var propertyValue = row[1];
// var myObject = allMyPropertiesInOneObject
myObject[propertyKey] = propertyValue;
}
ScriptProperties.setProperties(myObject);
}
Just to update this answer, Google has deprecated ScriptProperties and replaced it with the PropertiesService. You can solve the same problem using setProperties() on a Properties object, except you have to choose which kind of store you want using a call to PropertiesService first:
var myObject = {} ;
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var propertyKey = row[0];
var propertyValue = row[1];
myObject[propertyKey] = propertyValue;
}
var myProperties = PropertiesService.getScriptProperties(); // or getUserProperties() or getDocumentProperties()
myProperties.setProperties(myObject);
See the documentation for details. Note: this only seems to work for simple key/value pairs. When I try to set and get more complex JSON objects, with keys set to arrays of values, I can retrieve the object but not the values inside it. I can't use JSON.parse() on it successfully, either. I'll post again if I can figure it out.
Is there a way to run a piece of JavaScript code only ONCE, without using boolean flag variables to remember whether it has already been ran or not?
Specifically not something like:
var alreadyRan = false;
function runOnce() {
if (alreadyRan) {
return;
}
alreadyRan = true;
/* do stuff here */
}
I'm going to have a lot of these types of functions and keeping all booleans would be messy...
An alternative way that overwrites a function when executed so it will be executed only once.
function useThisFunctionOnce(){
// overwrite this function, so it will be executed only once
useThisFunctionOnce = Function("");
// real code below
alert("Hi!");
}
// displays "Hi!"
useThisFunctionOnce();
// does nothing
useThisFunctionOnce();
'Useful' example:
var preferences = {};
function read_preferences(){
// read preferences once
read_preferences = Function("");
// load preferences from storage and save it in 'preferences'
}
function readPreference(pref_name){
read_prefences();
return preferences.hasOwnProperty(pref_name) ? preferences[pref_name] : '';
}
if(readPreference('like_javascript') != 'yes'){
alert("What's wrong wth you?!");
}
alert(readPreference('is_stupid') ? "Stupid!" : ":)");
Edit: as CMS pointed out, just overwriting the old function with function(){} will create a closure in which old variables still exist. To work around that problem, function(){} is replaced by Function(""). This will create an empty function in the global scope, avoiding a closure.
I like Lekensteyn's implementation, but you could also just have one variable to store what functions have run. The code below should run "runOnce", and "runAgain" both one time. It's still booleans, but it sounds like you just don't want lots of variables.
var runFunctions = {};
function runOnce() {
if(!hasRun(arguments.callee)) {
/* do stuff here */
console.log("once");
}
}
function runAgain() {
if(!hasRun(arguments.callee)) {
/* do stuff here */
console.log("again");
}
}
function hasRun(functionName) {
functionName = functionName.toString();
functionName = functionName.substr('function '.length);
functionName = functionName.substr(0, functionName.indexOf('('));
if(runFunctions[functionName]) {
return true;
} else {
runFunctions[functionName] = true;
return false;
}
}
runOnce();
runAgain();
runAgain();
A problem with quite a few of these approaches is that they depend on function names to work: Mike's approach will fail if you create a function with "x = function() ..." and Lekensteyn's approach will fail if you set x = useThisFunctionOnce before useThisFunctionOnce is called.
I would recommend using Russ's closure approach if you want it run right away or the approach taken by Underscore.js if you want to delay execution:
function once(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
return memo = func.apply(this, arguments);
};
}
var myFunction = once(function() {
return new Date().toString();
});
setInterval(function() {console.log(myFunction());}, 1000);
On the first execution, the inner function is executed and the results are returned. On subsequent runs, the original result object is returned.
What about an immediately invoked anonymous function?
(function () {
// code in here to run once
})();
the code will execute immediately and leave no trace in the global namespace.
If this code is going to need to be called from elsewhere, then a closure can be used to ensure that the contents of a function are run only once. Personally, I prefer this to a function that rewrites itself as I feel doing so can cause confusion, but to each their own :) This particular implementation takes advantage of the fact that 0 is a falsy value.
var once = (function() {
var hasRun = 0;
return function () {
if (!hasRun) {
hasRun++;
// body to run only once
// log to the console for a test
console.log("only ran once");
}
}
})();
// test that the body of the function executes only once
for (var i = 0; i < 5; i++)
once();
Elegant solution from Douglas Crockford, spent some time to understand how it works and stumbled upon this thread.
So the wrapper once return function which is just invokes parameter's function you passed. And taking advantage of closures this construction replaced passed function to empty function, or null in original source, after the first call, so all the next calls will be useless.
This is something very close to all other answers, but it is kinda self containing code and you could use it independently, which is good. I am still trying to grasp all the entire mechanism of replacement, but practically it just works perfectly.
function once (func) {
return function () {
var f = func;
func = null;
return f.apply(this, arguments);
};
}
function hi(name) {
console.log("Hi %s", name);
}
sayonce = once(hi);
sayonce("Vasya");
sayonce("Petya");
for those who are curious here is jsbin transformations
(function (){
var run = (function (){
var func, blank = function () {};
func = function () {
func = blank;
// following code executes only once
console.log('run once !');
};
return function(){
func.call();
};
})();
run();
run();
run();
run();
})();
I just ran into this problem, and ended up doing something like the following:
function runOnce () {
if (!this.alreadyRan) {
// put all your functionality here
console.log('running my function!');
// set a property on the function itself to prevent it being run again
this.alreadyRan = true;
}
}
This takes advantage of the fact that Javascript properties are undefined by default.
In addition, the nature of what happens in the "/* do stuff here */" may leave something around that, when present, must mean that the function has run e.g.
var counter = null;
function initCounter() {
if (counter === null) {
counter = 0;
}
}
If not bound to an event, code is usually ran once
I'm somewhat new to object oriented programming in Javascript and I'm trying to build a handler object and library for a list of items I get back from an API call. Ideally, I'd like the library functions to be members of the handler class. I'm having trouble getting my class method to work however. I defined as part of the class bcObject the method getModifiedDateTime, but when I try to echo the result of the objects call to this method, I get this error:
Error on line 44 position 26: Expected ';'
this.getModifiedDateTime: function(epochtime) {
which leads me to believe that I simply have a syntax issue with my method definition but I can't figure out where.
response(
{
"items":
[
{"id":711,"name":"Shuttle","lastModifiedDate":"1268426336727"},
{"id":754,"name":"Formula1","lastModifiedDate":"1270121717721"}
],
"extraListItemsAttr1":"blah",
"extraListItemsAttr2":"blah2"
});
function response(MyObject) {
bcObject = new bcObject(MyObject);
thing = bcObject.getModifiedDateTime(bcObject.videoItem[0].lastModifiedDate);
SOSE.Echo(thing);
}
function bcObject(listObject) {
// define class members
this.responseList = {};
this.videoCount = 0;
this.videoItem = [];
this.responseListError = "";
// instantiate members
this.responseList = listObject;
this.videoCount = listObject.items.length;
// populate videoItem array
for (i=0;i<this.videoCount;i++) {
this.videoItem[i] = listObject.items[i];
}
this.getModifiedDateTime: function(epochtime) {
var dateStringOutput = "";
var myDate = new Date(epochtime);
dateStringOutput = myDate.toLocaleString();
return dateStringOutput;
};
}
You use = to assign values in JS, not ::
this.getModifiedDateTime = function(epochtime) {
You should use the = operator for methods defined as you did there (this.<methodName> = function (...) {).
The colon notation is used when declaring object literals.