Responding checkbox with withItemResponse - google-apps-script

My problem is:
const answers = sheet.getSheetValues(2, 2, sheet.getLastRow()-1, sheet.getLastColumn()-1); // Get the information from the sheet
formResponse[i].withItemResponse(items[19].asCheckboxItem().createResponse( answers[i][17])); // add a response to formResponse ,it is a checkbox item
answers[i][17] is an object actually. It has the value "FALSE". I get this error:
Cannot find method createResponse(string).
Even if i write false/true or "false"/"true" or something else , createResponse rejects it with error. When i use boolean i take the same error with the boolean version.
How should i add the checkbox as a response ? Thanks in advance.

I solved the problem with a weird way. Code:
if(answers[i][17] == true)
formResponse[i].withItemResponse(items[19].asCheckboxItem().createResponse( new Array(items[19].asCheckboxItem().getChoices()[0].getValue() )));
The reason behind this:
You need to give the string array, not string itself. And string must be the checkbox title, not true or false. Because we could add more than one checkbox and when it comes to checkboxes responses, how could we choose which one is true or false? So i took the choices and since i have one choice, instead of making it string array in a loop i decide to use only the first item. Since i have one item, array has one element. But it is choice array, so i took the first string and i put it in a new array. Here it is, you have a string array. For multiple checking, you can create a loop that iterates the choice array and add their value(which is string) to a new array. Like:
var strArray = new Array(choiceArray.length);
for(var i=0; i < choiceArray.length; ++i)
strArray[i] = choiceArray[i];
And you can disable some of the for letting it unchecked. It is just a way, you can do more efficient versions.
PS: I think that google apps script has things that enforce the developers to write non-efficient and too many lines of codes. But at the end, the fundamental system is working great and actually if some of us decide to use another language or framework, it could be much slower.

Related

Convert List<dynamic> to List<String>

I am getting data from server. The run runtimeType shows that they have type List.
Currently I am using cast<String>() to get List<String>.
But is it's only\right way?
var value = await http.get('http://127.0.0.1:5001/regions');
if(value.statusCode == 200) {
return jsonDecode(value.body)['data'].cast<String>();
}
There are multiple ways, depending on how soon you want an error if the list contains a non-string, and how you're going to use the list.
list.cast<String>() creates a lazy wrapper around the original list. It checks on each read that the value is actually a String. If you plan to read often, all that type checking might be expensive, and if you want an early error if the last element of the list is not a string, it won't do that for you.
List<String>.from(list) creates a new list of String and copies each element from list into the new list, checking along the way that it's actually a String. This approach errs early if a value isn't actually a string. After creation, there are no further type checks. On the other hand, creating a new list costs extra memory.
[for (var s in list) s as String],
[... list.cast<String>()],
<String>[for (var s in list) s],
<String>[... list] are all other ways to create a new list of strings. The last two relies on implicit downcast from dynamic, the first two uses explicit casts.
I recommend using list literals where possible. Here, I'd probably go for the smallest version <String>[...list], if you want a new list. Otherwise .cast<String>() is fine.

SAPUI5 copy model and break binding?

On initialization I read an oData service to get a small list of values and I store the model for further use in the application.
sap.ui.getCore().setModel(oODataJSONModel, "xlist");
At multiple stages, I want to make a copy of the original model, make changes to the values list and use it in a Select drop down. I've tried multiple different things, but every time I update/delete the copied model values, it is instantly reflected in the original model. This seems like a simple ask, but is there a way to break the link between the original model and the copied model, ideally I want to keep the original list intact so that list can be re-used over and over, regardless of what changes are made to the copies?
var oModelCpy = new sap.ui.model.json.JSONModel();
var cpyModelArray = oOrigModel.getData();
cpyModelJsonData = { results : [ cpyModelArray ] };
oModelCpy.setData(cpyModelJsonData );
When I remove entries from the copy model, it also removes entries from the original model, which in this case is not what i want.
Any suggestions?
A better approach is to save your data in the success handler:
oODataJSONModel.read("/yourService",
null,
null,
false,
function(oData, oResponse){
var oODataJSONModel = new sap.ui.model.json.JSONModel();
oODataJSONModel.setData(oData);
this.getView().setModel(oODataJSONModel, "jsonModel");
}
);
EDIT
I just stumbled upon this question while I was browsing through the list of UI5 questions, and it dawned to me what is causing your underlying copy issue! :-)
If you copy an array of objects to a new array (which is also happens if you copy model data to another model), you won't get a new array with new objects
Instead, you actually will get a new array, but with references to the old objects. So any change you make to a value in an object inside an array in model 1, will end up having that same value in model 2
So, in effect, you need to create new objects based on the old ones. Luckily, you don't need costly for loops and hardcoded value-copying logic to achieve this; one single line should be ok.
Let's say your original data is referenced by an array aData.
You then copy this data (a true copy) to a new array using JSON:
var aDataCopy = JSON.parse(JSON.stringify(aData));
If you now set this aDataCopy as the data for your second model, it will not have any references to the old model anymore.
Hope this helps!
Try using jquery extend() method to make a copy of the data. I had similar troubles earlier.
var newObject = $.extend({},oldObject);
Try this for once. Find the reference at http://api.jquery.com/jquery.extend/

Understanding google spreadsheets set vs get logic

I'm desperately trying to learn to "think" in this language (my native coding language is VBA/ASP).
Here is a very basic example that simply reads the contents of a specific cell in Sheet1 and then assigns that value to a variable named rngVal
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("Sheet1"));
var rngVal = ss.setActiveSelection("A1").getValues();
When I attempt to deconstruct this very simple 3-line piece of code (in the interest of understanding it's purpose), I admit to being rather perplexed.
Here's why:
When I insert a msgbox after each line, I get this:
var ss = SpreadsheetApp.getActiveSpreadsheet(); = "Spreadsheet"
ss.setActiveSheet(ss.getSheetByName("Sheet1")); = "Spreadsheet"
var rngVal = ss.setActiveSelection("A1").getValues(); = The cell value
What is "Spreadsheet"?
How is this helpful(?) and how can I use it for the benefit of navigating the worksheet and reading/writing values within it?
That's 2 lines of code that does what(?), and for what benefit?
(I'm not trying to be combative, I'm just trying to understand so that I can learn to "think" in this language)
Secondly:
In the first line, what am I get ting?
In the second line I'm first set 'ting, and then I'm get 'ting? (I'm very confused about what that line is actually doing)
In the third (last) line there is more set 'ting and get 'ting, although this time it's not nested.
This is very basic code (functionally), but I'm having trouble grasping it's logic in terms of how to "think" using that logic.
Is there anyone out there who would be kind enough to show some patience and help me by describing the step by step logic of this simple code of selecting a cell, capturing it's value, and then assigning it to a variable?
Please understand, that when I "think" of how to do that very simple task, I can do it in one very concise line (in VBA)...
rngVal = Sheets("Sheet1").Range("A1").Value
...therefore I am really confused by all of the "Spreadsheet" stuff and the need for 3 lines filled with set's and get's.
Anyone? Please?
Hmm somehow the correct answer (my other one) got voted down. Again its pointless to do setactiveselection, there is no need to change the selected cell it just makes your code slower.
Google uses Javascript Classes to represent these different Objects.
I've split line 3 into two parts to help with my explanation. Please compare:
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("Sheet1"));
var rng = ss.setActiveSelection("A1");
var val = rng.getValues();
Each line above returns a different class of Object. The first line returns an Object with class Spreadsheet, the second line returns an object of class Sheet, the third line returns an object of class Range, and the final line returns an object of class Object.
Take a look here for documentation on the various actions (methods) that can be performed on/with the different classes of objects. When I'm building a Google App, I live on that webpage.
To compare the above with the code you understand from VBA, this line:
rngVal = Sheets("Sheet1").Range("A1").Value
Could be written in Javascript in one line as follows:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").setActiveSelection("A1").getValues();
Hope that helps. Let me know if you have questions.
You guys are doing it more complex than neeeded. No need to mess with selection.
Getactivespreadsheet ().getsheetbyname ("x").getrange ("a1").getValue () also works.

How can I iterate Dynamic object in Haxe

I have Object parsed from JSON (haxe.Json.parse()) and I need to iterate over it.
I already tried to cast this object to Array<Dynamic>:
var data:String='{"data":{"0":0,"1":1},"method":"test"}';
var res:{method:String,data:Array<Dynamic>} = haxe.Json.parse(data);
for (n in res.data)
trace('aa')
There is no Can't iterate dynamic exception, just not working (iterating).
I completley don't understand why in Haxe iterating procedure is so difficult.
For the sake of posting a complete answer, and in case other people are wondering
In your first example, you've told the compiler that "res" contains two properties - one called "method" (which is a String) and one called "data" (which is Array). Now the JSON you're using doesn't actually have an Array<Dynamic>, it just has a dynamic object. An Array would look like: "data":[0,1].
So, assuming you meant for the JSON to have data as a Dynamic object, here is how you loop over it, using Reflect (as you mentioned in the comments):
var data:String='{"data":{"0":0,"1":1},"method":"test"}';
var res = haxe.Json.parse(data);
for (n in Reflect.fields(res.data))
trace(Reflect.field(res.data, n));
Note here we don't have to specify the type of "res", since we're using Reflection just leaving it as Dynamic will be fine.
Now, if your JSON actually contains an Array, the code might look like this:
var data:String='{"data":[0,1],"method":"test"}';
var res:{method:String,data:Array<Int>} = haxe.Json.parse(data);
for (n in res.data)
trace(n);
Here you use explicit typing to tell the compiler that res.data is an Array (and this time it actually is), and it can loop over it normally.
The reason you didn't get an error at compile-time is because the compiler thought there was genuinely going to be an array there, as you told it there was. At runtime, whether or not it throws an exception probably depends on the target... but you probably want to stay out of that anyway :)
Demo of both styles of code: http://try.haxe.org/#772A2

AS3/Flex apply sort to ArrayCollection only once

I've got an ArrayCollection that serves as a dataProvider for a list.
The collection stores objects of type MyObject:
public class MyObject {
public var myMap:Dictionary;
}
myMapstores key-value pairs, the key being an integer, the values are Strings.
So far for the constraints. What I want to do now is to sort the collection based on fields of the map.
Using a the ArrayCollection's sort function with my own compareFunction does work. This is how I've implemented it:
var key:int = 15;
var sort:Sort = new Sort();
sort.compareFunction = fidSort;
myCollection.sort = sort;
myCollection.refresh();
private function fidSort(a:Object, b:Object, fields:Array = null):int {
if(a.myMap[key].fieldValue == b.myMap[key].fieldValue) {
return 0;
} else if(a.myMap[key].fieldValue > b.myMap[key].fieldValue) {
return 1;
} else{
return -1;
}
}
As I said, that does work for the sake of sorting. However, naturally the sort (being a property of the collection) remains on the collection unless specifically removed from it, which means that every time a value in the map of MyObject changes, it will get sorted according the comparefunction.
What I need is to apply the sort exactly once, what happens afterwards with the map values shouldn't change the collections sorting.
I've tried things like disabling autoupdate on the colleciton (naturally that won't work as the collection doesn't get any updates any more (well it does, but they are cached only)).
After that I've read this post about sorting the underlying array.
However, that doesn't seem to work with the map, as I do get a compile error saying that the myMap[key].fieldValue couldn't be found on MyObject.
So yes, I'm kinda lost in space here. If someone has a clue how to achieve this, very basic task really, please let me know.
Cheers!
Got it, and for the sakes of completeness, I'd like to answer this question myself.
As said before, using myCollection.toArray().sort(fidSort) didn't work completely. The array made in this step has indeed been sorted, the collection, however, didn't get the sort, even though refresh() has been called.
To fix this, instead of creating a new array from the collection, we need to directly use the collection's source (which is an array of course) and sort that array;
collection.source.sort(fidSort);
collection.refresh();
Since we are still only sorting the array and not applying the Sort to the collection itself, the collection is sorted only once, regardless of the updates to it's data.
Edit: Just for kicks, restoring the original item positions isn't possible out of the box when sorting the collection's underlying array like it can be done when applying a sort on an ArrayCollection directly and setting it to null to restore the positions.
Simple solution is to cache the array item indices beforehand.