List and map elements in DekiScript
- Applies to:
- All MindTouch Versions
- Role required:
- Draft Contributor
This page demonstrates all the basic operations you'd ever want to do to list and map elements. If you do any serious Dekiscripting, you will probably end up needing to use most of all of these.
Create lists and maps
This is simple stuff. We will use these examples as fodder for our various manipulations later on this page.
Lists
var myList = [ "zero", "one", "two", "three" ];
Maps
var myMap = { one:1, two:2, three:3 };
If you want to use a key name stored in a variable, enclose it in parentheses:
var keyName = "one"; var myMap = { (keyName):1, two:2, three:3 };
Access elements
By "accessing", we mean using an element in a right-side expression. These forms are not used to set element values, only read them.
Lists
Accessing a list element is straightforward:
var listElement = myList[0]; // = "zero"
Of course, the subscript can be any expression:
var subscript = 0; var listElement = myList[subscript+1]; // = "one"
Maps
There are two ways to access map elements. If the key is a known constant:
var mapElement = myMap.one; // = 1
If the keyname is stored in a variable, then use list-like syntax ("bracket notation"):
var keyName = "one"; var mapElement = myMap[keyName]; // = 1
Of course you can use bracket notation with a constant keyname as well, if you are so inclined; just remember to enclose the constant in quotes:
var mapElement = myMap["one"]; // = 1
Size of a list or map
For both lists and maps, the syntax for finding the number of elements is the same:
var myListLength = #myList; var myMapLength = #myMap;
For a list, then, the easiest way to access the last element is:
var myLastElement = myList[#myList-1];
The list.splice() function
We now need to take a moment to talk about the list.splice() built-in function, which is going to get a workout when we start modifying lists below. In the examples below. This function does two things:
- Remove element(s) if desired from a specified index in a list
- Insert element(s) if desired to that same index
The key thing to remember here is that the insertions and deletions occur at the same index. By combining insertions and deletions, we can do most of what we'd want, even though it would not necessarily be the first thing we'd think to try.
Add new elements
The primary (but not the only) way to add a new element to either a list or map is to concatenate a list with the new element(s).
Lists
To add a new element to a list:
let myList ..= [ "four" ];
Of course you can add more than one at a time:
let myList ..= [ "four", "five" ];
We can also use the all-powerful list.splice() function if we want to add new item(s) to a specific point in the list:
{{ var myList = [ "one", "two", "three", "four" ]; let myList = list.splice(myList, 1, 0, [ "one point five" ]); myList; }} |
[ "one", "one point five", "two", "three", "four" ] |
Maps
The approach is similar:
let myMap ..= { four:4 }; let myMap ..= { five:5, six:6 };
And remember that the keyname can be an expression if enclosed in parentheses:
var keyName = "four"; let myMap ..= { (keyName):4 };
Modify existing elements
Lists
Modification of list elements can be done with list.splice(). By removing an element and then inserting a new one at the same location, we can get the job done. To capitalize the item at index 2:
let myList = list.splice(myList, 2, 1, [ toupper(myList[2]) ]);
That is a little ungainly, but it works.
Maps
You can modify a map element by concatenating a list with the new value:
let myMap ..= { one:"uno" };
If the key already exists, this will re-assign the value. So I could increment a numeric value like this:
let myMap ..= { one:myMap.one+1 };
Delete elements
Lists
There are two ways to remove elements from a list. If you'd like to so based on the index (or indices) of the elements, then use list.splice(). Let's remove the second element from our list:
let myList = list.splice(myList, 1, 1);
To remove elements based on their value, use list.select(). Let's remove all elements with value "one":
let myList = list.select(myList, "$ != 'one'");
Maps
For maps, we will have to depend on map.select(). To remove one element, we will define a test that matches on everything but the one we want to remove. So let's remove element "one" based on its key:
let myMap = map.select(myMap, "$.key != 'one'");
Now let's remove all elements with the value "1":
let myMap = map.select(myMap, "$.value != 1");
Not very pretty, but it gets the job done in a pinch. Of course, you can do a lot more with list.splice(), list.select(), and map.select() then just remove individual items from a list or map.
Convert between lists and maps
There are a lot of ways to convert between lists and maps; the best way depends on the exact structure of your list or map, and what you are trying to accomplish. Here are some common examples.
To perform a basic conversion from a map to a list, use map.keyvalues(). This creates a list of maps as follows:
{{ var myMap = { one:1, two:2, three:3 }; var myList = map.keyvalues(myMap); myList; }} |
[ { key : "one", value : 1 }, { key : "two", value : 2 }, { key : "three", value : 3 } ] |
This is actually a pretty useful form, since we can then use list.sort() to sort it by value.
Going from a list to a map is actually a bit trickier, and heavily dependent on what you are trying to do. Most solutions will require iterating through the list, and creating a map entry for each. Here's an example:
{{ var myList = [ "zero", "one", "two", "three" ]; var myMap = {}; foreach (var e in myList) { let myMap ..= { (e):__count }; } "myList="; myList; web.html("<br />"); "myMap="; myMap; }} |
myList=[ "zero", "one", "two", "three" ] myMap={ one : 1, three : 3, two : 2, zero : 0 } |
Get distinct values from a list
There is no built-in function to get distinct values in a list like you can in SQL. However, by combining some List and Map techniques you can do this:
{{ var myList = [1,3,3,4,2,2,5]; map.keys(List.Groupby(myList,"$")); }} |
[ "1", "3", "4", "2", "5" ] |
Note that this will always return a list of strings, since the keys of the map that list.groupby() returns are always strings. If your original list is filled with strings, or if you do not care about the type of the returned values, that is no problem.
If you want to retain the type of the original data, you can use this slightly more complex approach, using a List Constructor:
{{ var myList = [1,3,3,4,2,2,5]; [ k[0] foreach var k in List.Groupby(myList,"$") ]; }} |
[ 1, 3, 4, 2, 5 ] |
Remember that when we iterate over a map, DekiScript automatically gives us the values, which is why we didn't need to use a map.values() call in the second example.