Convert List<dynamic> to List<String> - json

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.

Related

Responding checkbox with withItemResponse

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.

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.

Map and Filter in Haskell

I have two lists of tuples which are as follows: [(String,Integer)] and [(Float,Integer)]. Each list has several tuples.
For every Integer that has a Float in the second list, I need to check if its Integer matches the Integer in the first list, and if it does, return the String - although this function needs to return a list of Strings, i.e. [String] with all the results.
I have already defined a function which returns a list of Integers from the second list (for the comparison on the integers in the first list).
This should be solvable using "high-order functions". I've spent a considerably amount of time playing with map and filter but haven't found a solution!
You have a list of Integers from the second list. Let's call this ints.
Now you need to do two things--first, filter the (String, Integer) list so that it only contains pairs with corresponding integers in the ints list and secondly, turn this list into just a list of String.
These two steps correspond to the filter and map respectively.
First, you need a function to filter by. This function should take a (String, Integer) pair and return if the integer is in the ints list. So it should have a type of:
check :: (String, Integer) -> Bool
Writing this should not be too difficult. Once you have it, you can just filter the first list by it.
Next, you need a function to transform a (String, Integer) pair into a String. This will have type:
extract :: (String, Integer) -> String
This should also be easy to write. (A standard function like this actually exists, but if you're just learning it's healthy to figure it out yourself.) You then need to map this function over the result of your previous filter.
I hope this gives you enough hints to get the solution yourself.
One can see in this example how important it is to describe the problem accurately, not only to others but foremost to oneself.
You want the Strings from the first list, whose associated Integer does occur in the second list.
With such problems it is important to do the solutions in small steps. Most often one cannot write down a function that does it right away, yet this is what many beginners think they must do.
Start out by writing the type signature you need for your function:
findFirsts :: [(String, Integer)] -> [(Float, Integer)] -> [String]
Now, from the problem description, we can deduce, that we essentially have two things to do:
Transform a list of (String, Integer) to a list of String
Select the entries we want.
Hence, the basic skeleton of our function looks like:
findFirsts sis fis = map ... selected
where
selected = filter isWanted sis
isWanted :: (String, Integer) -> Bool
isWanted (_,i) = ....
You'll need the functions fst, elem and snd to fill out the empty spaces.
Side note: I personally would prefer to solve this with a list comprehension, which results often in better readable (for me, anyway) code than a combination of map and filter with nontrivial filter criteria.
Half of the problem is to get the string list if you have a single integer. There are various possibilities to do this, e.g. using filter and map. However you can combine both operations using a "fold":
findAll x axs = foldr extract [] axs where
extract (a,y) runningList | x==y = a:runningList
| otherwise = runningList
--usage:
findAll 2 [("a",2),("b",3),("c",2)]
--["c","a"]
For a fold you have a start value (here []) and an operation that combines the running values successively with all list elements, either starting from the left (foldl) or from the right (foldr). Here this operation is extract, and you use it to decide whether to add the string from the current element to the running list or not.
Having this part done, the other half is trivial: You need to get the integers from the (Float,Integer) list, call findAll for all of them, and combine the results.

applying separation of concerns

I wonder if you think that there is a need to refactor this class.( regarding separation of concern)
publi class CSVLIstMapping<T>
{
void ReadMappingFromAttirbutes();
void GetDataFromList();
}
ReadMappingFromAttributes - Reads the mapping from the type T and stores it in the class. Has a name of the list to use and a number of csvMappingColumns which contains the name of the property to set the value in and the name of csvcolumns.
GetObjectsFromList - uses a CVSListreader ( which is passed in via the constructor) to get the data from all row's as KeyValuePair ( Key = csvcolumnName , value = actually value) and after that it uses the mappinginformation( listname and csvMappingColumns ) to set the data in the object.
I cant decide if this class has 2 concerns or one. First I felt that it had two and started to refactor out the conversion from rows to object to another object. But after this it felt awkward to use the functionality, as I first had to create a mappingretriver, and after that I had to retrive the rows and pass it in together with the mapping to the "mapper" to convert the objects from the rows
/w
Sounds like two concerns to me: parsing and mapping/binding. I'd separate them. CSV parsing should be a well-defined problem. And you should care about more than mere mapping. What about validation? If you parse a date string, don't you want to make sure that it's valid before you bind it to an object attribute? I think you should.
Rule of thumb: if it's awkward, it's wrong.
I have to say I'm finding it hard to understand what you've written there, but I think it's likely that you need to refactor the class: the names seem unclear, any method called GetFoo() should really not be returning void, and it may be possible that the whole ReadMappingFromAttribute should just be constructor logic.