Introduction
Using Xill, it is important to be able to work with list and object variables, together also called 'collections'. They are used often when processing information that is not only atomic (like strings and numbers) but also part of a data structure or record that belongs together, like a field/row from a database. Also, since functions can only return one variable, and automating tedious tasks usually involves looping through lists, you will soon need variables that are more complex than the atomic ones.
Difference between list and object
They both consist of a set of variables, which can also be collections themselves. The essential difference is that objects are key-value pairs, having a chosen string as key/index, while lists are only a numbered set of elements.
If you know Java, the best way to put it is that a list is an Array and an object is a Map.
Declaration
The simplest way to make new collections goes like this:
use System;
use Web;
use Collection;
//make a new list
var new_list = [
"a",
"b"
];
//make a new object
var new_object = {
"A" : "a",
"B" : "b"
};
System.print(new_list);
System.print(new_object);
Put this code in an empty robot, step through it using a breakpoint and the step-in button, and look at the previews in the debug panel at the right. If you haven't done this before, get back to the getting started guides.
JSON
Did you notice that the used type of brackets is different for objects than for lists? This is because Xill follows the JSON standard.
Also, note that the string representation for declaration is the same as that of the output.
Adding items
Adding elements is only possible at the end (bottom) of a collection. Add these examples to your robot and step through it to see what happens.
//adding a string to a list
new_list[] = "c";
//adding a string to an object
new_object["z"] = "d";
//and an alternative way
new_object.zz = "dd";
System.print(new_object);
Complex objects
Collections can not only contain string elements, but will also hold any other variable for you. Add the following to your robot:
//adding different types
var new_sublist = ["q", 5, "5"];
new_object["sublist"] = new_sublist;
new_object["googlepage"] = Web.loadPage("http://www.google.com");
System.print(new_object);
As you can see, object elements can be lists too, or any atomic value, like a webpage. If you run with a breakpoint at the last line, your debug panel should look like this:
The same thing can be done the other way around; objects can be nested in a list.
Adressing elements
Using one element
There are two ways to use an element directly; with a bracket and with a dot. The bracket method works for arrays as well. Try it out yourself.
//logging object items
System.print(new_object["z"]);
System.print(new_object.zz);
//logging the second list item
System.print(new_list[1]);
Use in loops
Collections are often used in foreach() loops. That may not make much sense for our current example, but it's the simplest way to show it, so we are just going to log every element:
//logging all object items
foreach(index,item in new_object) {
System.print("On index '" :: index :: "' of new_object, there's the item '" :: item :: "'");
}
//logging all array items
foreach(index,item in new_list) {
System.print("On index '" :: index :: "' of new_list, there's the item '" :: item :: "'");
}
Notes:
- You might have noticed that the google page is not logged, but only the url. This is because this simple representation has been chosen for the conversion of the page to a string. At the time of writing, there is no full preview for variables like this. You can, however, use Web.getText(new_object.googlepage) to take the source html from the page.
- Make sure to use good variable names in practice. So no 'item' and 'new_object', but names that are meaningful in the context of the project you are doing.
Removing items
You can remove the first items of both collections with the following code:
//remove object item with key 'A'
Collection.remove(new_object,"A");
//remove first list item
Collection.remove(new_list,0);
System.print(new_object);
System.print(new_list);
Changing items
Since indices/keys have to be unique, changing an item is the same as adding an item on an existing index. So in the case of this example, that could be as follows.
//change the first item
new_list[0] = "booyah";
//change the item on index "googlepage"
new_object.googlepage = 404;
If you're done with all previous lines of code, you can throw it away now.
Changing collection variables in a new context
There's one other thing fundamentally different between collections and atomic variables. It's actually the same difference that Java has between primitive variables and objects.
When 'copying' a collection (like collection1 = collection2;
) or passing a collection to a routine (doSomething(collection1);
), the list itself will not be copied or passed, but an extra reference will be created and used. In the following example, there's only one list, but at a certain point three references exist. The names of them are numbered. Start a new robot with this code:
use System;
var list1 = ["a"];
var list2 = list1;
doSomething(list2);
System.print(list1);
function doSomething (list3) {
//adding an item to the list that 'list3' references
list3[] = "b";
}
It looks like 'b' is added to list1, even though it might seem we never touched that list after its assignment.
The point is that there is only one list, and 'list1' is only a name/reference and not the list itself. That does not mean that you're passing by reference though.
Java and Xill only pass by value, so 'list3' is a copy of the reference to the list. That's why in the following example list1 does not change:
use System;
var list1 = ["a"];
var list2 = list1;
doSomething(list2);
System.print(list1);
function doSomething(list3) {
//assigning list3 again, so the original reference to the list is lost
list3 = ["b"];
}
Hopefully, if you didn't already get this from Java knowledge, you'll understand now that list variables are always references and how to handle them. Read more about passing objects
Functions involving collections
Collection.duplicate(collection)
This simply returns a copy of the collection. Or better said, with the above in mind: it creates a copy of the collection and returns a reference to it. This is a new pointer to a completely new collection, so both collections can be edited independently.
Sorting functions
Collection.reverse()
and Collection.sort()
are available to do some sorting. Read the Content Tools built-in documentation to see what exactly they do.
Collection.contains()
With this one, you can check if some atomic value exists in a collection.
System.parseJSON()
Give this function a string that complies to JSON and it will return a collection. System.toJSON does the opposite and can also pretty-print.
Database objects, iterables
Database objects are usually represented by an object. Database.getobject() and Document.get() return an object if there's a result with more than one field (or an atomic in case of only one field).
For performance- and stability reasons, Database.query() and Document.find() always returns an iterable. This is practically the same as an object, with the core differences that the values are not pre-loaded in Xill IDE's memory and the variabletype is ATOMIC. Therefore, you cannot view the contents of an iterable while debugging. It's possible to get the first query result with iterable_variable[0], but that's not advisable since you won't know for sure there's actually an element in it. If not, an error message will be displayed.
The way to safely work with those variables is by iterating through it with a foreach() loop. Iterating through an iterable works no different from iterating through a collection, but here's an example for your visualisation:
use Database;
var queryresults = Database.query("SELECT * FROM table");
// if you looked at the variable 'queryresults' in the debug panel here,
// you'll only see the query and not the results
foreach(index, result in queryresults) {
doSomeThing(result);
//if 'table' has more than one field and at least one row, 'result' will be an object
//if 'table' has only one field, 'result' will be atomic
//if 'table' has no rows, this line won't be reached at all
}
Other collection encounters
Besides explicitly building them or getting them from a database, there are more ways to end up with these data types. You will get a collection as a result of a couple of built-in functions. This one, for instance, gives you a big object with info about your computer:
var info = System.info();
String.split()
and String.allMatches()
are very useful opposing text functions. With a regex as parameter, you specify which parts of a string you want (or don't want) to be returned in a list.
You might encounter APIs that deliver in a form of json, like the Open Movie Database. Choosing the xml option they also offer might be easier, but this is a list/json tutorial after all. Try this out:
use System;
use Web;
//set a file path you like
var temp_path = "D:/temp/collectiontutorial.json";
//load the API item
var jsonpage = Web.loadPage("http://www.omdbapi.com/?t=back+to+the+future&y=&plot=full&r=json");
var jsontext = Web.getText(Web.xPath(jsonpage,"//body"));
var jsonobject = System.parseJSON(jsontext);
System.print("The movie '" :: jsonobject.Title :: "' was directed by " :: jsonobject.Director
:: " and got a rating of " :: jsonobject.imdbRating :: " on imdb.");