It’s often useful for an object to maintain a record of avatars or objects. One example might be a shop vendor which keeps a list of who has bought which item from it, or perhaps you want something which will tell you who has visited your shop recently.

There are various ways of doing this, but all of them eventually require you to store the information somewhere, and short of providing a link to an off-world database (a perfectly viable solution, but beyond the scope of this blog), the best option is to use a list.

The LSL implementation of lists is relatively primitive, but quite flexible, especially once you get to grips with the various associated functions, of which there are quite a few.

A list is just what the name implies. It is a simple collection of values, of any data type.

Declaring a list variable, and adding some items, is quite simple:

list animals = [];    // This creates an empty list.
animals += ["cat"];   // This adds one entry.
animals += ["dog"];   // This adds another entry.

Our list is now [“cat”, “dog”]. We can add more than one item at once:

animals += ["rabbit", "mouse"];

The list now holds [“cat”, “dog”, “rabbit”, “mouse”].

If we want to get a particular item out of a list, we use one of the llList2… functions. As our list only holds strings, we use llList2String. This needs to know the list that we are extracting from, and the item number. Be careful – items in a list are numbered from 0! If you have never done any programming before, this might catch you out, but it is very common in programming languages (for reasons that I won’t go into here):

// Copies "dog" to the 'item' variable.
string item = llList2String(animals, 1);

Actually, a list can hold any data type, and any mixture of data types, except (and this is an important exception) other lists.

For example, we could do this:

animals += [10.12, 16, "kangeroo"];

Our list is now [“cat”, “dog”, “rabbit”, “mouse”, 10.12, 16, “kangeroo”]. As you’ll realise, this probably isn’t a clever thing to do, because we now have strings and numbers mixed in our list, in no particular order.

Assuming that we do know the order, we can extract these values using the llList2Float and llList2Integer functions:

float fvalue = llList2Float(animals, 4);
integer ivalue = llList2Integer(animals, 5);

On the other hand, suppose we want to store several items of information for each ‘entry’ in our list. Suppose we want a script that holds avatar names along with some kind of count for each avatar (maybe the number of times they have visited your shop).

What we need is a strided list. This is exactly the same as a normal list, but we impose certain conditions on it, and use a slightly more convoluted way to extract information out of it.

Let me explain. To make things easier, let’s use some standard computer parlance, and call our combination of avatar id and count as a ‘record’. Our constraint on the list is that we always add a complete record at a time, and this record must always contain the same number of items, each item being of a specified type. In our example, a record is a list which contains a string (the avatar name) and an integer (the count).

For example:

list avatarList = []; // Create an empty list.
avatarList += ["Pyter Morgridge", 0];   // Add a record.
avatarList += ["Someone Else", 0]; // Add another record.

Our list now holds [“Pyter Morgridge”, 0, “Someone Else”, 0]. Because we have imposed a structure on it, we can extract a complete record out of the list by using the llList2List function, which extracts a sublist. In this case, we will extract the first record, which starts at position 0 in the list, and ends with position 1.

list pytersrecord = llList2List(avatarList, 0, 1);

We now have [“Pyter Morgridge”, 0] in pytersrecord. The only tricky bit (and it isn’t that tricky) is working out where a particular record starts.

We deal with than by making things a little more generic. Let’s start a new script:

integer FIELD_COUNT = 2;  // Number of items in each record.
list avatarList = []; // Create an empty list.

Now we can create a utility function that will return the position in the list for a particular record number. In our example above, the record for Pyter is record number 0, and the record for Someone Else is record number 1. The list entries for them start at 0 and 2 respectively.

integer getPosition(integer recordNumber)
{
    return recordNumber * FIELD_COUNT;
}

We can use this to create functions that will return a single record, delete a record, and update a record:

list getRecord(integer recordNumber)
{
    integer pos = getPosition(recordNumber);
    if (pos < (llGetLength(avatarList) + FIELD_COUNT - 1))
      return llList2List(avatarList, pos, pos + FIELD_COUNT - 1);
    else
      return [];  // Or possibly raise an error.
}
deleteRecord(integer recordNumber)
{
    integer pos = getPosition(recordNumber);
    if (pos < (llGetLength(avatarList) + FIELD_COUNT - 1))
      avatarList = llDeleteSubList(avatarList, pos, 
                                   pos + FIELD_COUNT - 1);
}
updateRecord(integer recordNumber, list newRecord)
{
    integer pos = getPosition(recordNumber);
    if (pos < (llGetLength(avatarList) + FIELD_COUNT - 1))
      avatarList = llListReplaceList(avatarList, newRecord, pos, 
					      pos + FIELD_COUNT - 1);
}

You’ll find more information about lists and strided lists here:

LSL Wiki – Lists