Class 7 of 8

Lists & String Functions

Store collections of data and manipulate text like a pro.

Step 1 of 8
Step 1

The list Type

A list is an ordered collection of values. Unlike a variable that holds one thing, a list can hold many — and they can even be different types.

LSLDeclaring lists
default
{
    state_entry()
    {
        // A list of strings
        list colours = ["red", "green", "blue", "gold"];

        // A list of integers
        list scores = [10, 25, 50, 100];

        // A mixed list (valid in LSL!)
        list mixed = ["Blackthorn", 42, 3.14, TRUE];

        llOwnerSay("Colours list created with " +
                   (string)llGetListLength(colours) + " items.");
    }
}
Memory note

Lists are stored in script memory. Each item uses roughly 16–64 bytes depending on type. For scripts with strict memory limits, keep lists small and delete items you no longer need.

Step 2

llGetListLength

llGetListLength(list) returns the number of items in a list. List indices in LSL are zero-based — the first item is at index 0, the last is at index length - 1.

LSLList length and indexing
default
{
    state_entry()
    {
        list names = ["Alice", "Bob", "Carol", "Dave"];

        integer len   = llGetListLength(names);  // 4
        string  first = llList2String(names, 0); // "Alice"
        string  last  = llList2String(names, len - 1); // "Dave"

        llOwnerSay("Count: " + (string)len);
        llOwnerSay("First: " + first);
        llOwnerSay("Last:  " + last);
    }
}
Step 3

Extracting Values — llList2*

Because lists can hold mixed types, you use typed extraction functions to pull values out. Using the wrong function returns a default value silently, so use the right one:

List extraction functions
llList2String(list, index)
Get item as a string
llList2Integer(list, index)
Get item as an integer
llList2Float(list, index)
Get item as a float
llList2Key(list, index)
Get item as a key (UUID)
llList2Vector(list, index)
Get item as a vector
LSLExtracting typed values
default
{
    state_entry()
    {
        list scores = [10, 25, 50, 100];
        integer i;

        for (i = 0; i < llGetListLength(scores); i++)
        {
            integer score = llList2Integer(scores, i);
            llOwnerSay("Score " + (string)i + ": " + (string)score);
        }
    }
}
Step 4

Modifying Lists

Lists in LSL are immutable values — you can't change a single item in-place. Instead, you build a new list and assign it back. This sounds awkward but becomes natural quickly.

LSLAdding and removing list items
default
{
    state_entry()
    {
        list items = ["apple", "banana", "cherry"];

        // Add an item to the end
        items = items + ["date"];
        llOwnerSay("After add: " + (string)llGetListLength(items) + " items");

        // Remove item at index 1 ("banana") using llDeleteSubList
        items = llDeleteSubList(items, 1, 1);
        llOwnerSay("After remove: " + llList2String(items, 0) +
                   ", " + llList2String(items, 1) +
                   ", " + llList2String(items, 2));
        // apple, cherry, date
    }
}
Step 5

llListFindList

llListFindList(haystack, needle) searches for a sub-list within a list and returns the starting index, or -1 if not found. This is your membership check.

LSLVIP access list check
list vipList = [];   // Filled with owner keys at state_entry

default
{
    state_entry()
    {
        // Populate the VIP list with key strings
        vipList = [(string)llGetOwner(), "fake-key-example-here"];
    }

    touch_start(integer num_detected)
    {
        string toucherKey = (string)llDetectedKey(0);

        // Search for the toucher's key in the list
        integer idx = llListFindList(vipList, [toucherKey]);

        if (idx != -1)
        {
            llSay(0, "Welcome, VIP!");
        }
        else
        {
            llSay(0, "Sorry, VIPs only.");
        }
    }
}
Tip

Note that the needle argument is a list too — wrap the value you're searching for in square brackets: llListFindList(myList, [searchValue]).

Step 6

llStringLength & String Tests

llStringLength(string) returns the number of characters in a string. Use it to validate input or test for an empty string:

LSLString length and empty check
default
{
    listen(integer channel, string name, key id, string message)
    {
        if (llStringLength(message) == 0)
        {
            llOwnerSay("Empty message received — ignoring.");
            return;
        }

        if (llStringLength(message) > 100)
        {
            llOwnerSay("Message too long: " + (string)llStringLength(message) + " chars.");
            return;
        }

        llOwnerSay("Got message: " + message);
    }
}
Step 7

llToUpper, llToLower, llStringTrim

These three functions handle case conversion and whitespace trimming — essential for comparing user input reliably.

LSLString transforms
default
{
    listen(integer channel, string name, key id, string message)
    {
        // Trim leading/trailing spaces and convert to lowercase
        // STRING_TRIM removes both leading and trailing whitespace
        string clean = llStringTrim(llToLower(message), STRING_TRIM);

        // Now comparisons work regardless of case or extra spaces
        if (clean == "hello")
        {
            llSay(0, "Hello back, " + name + "!");
        }
        else if (clean == "bye")
        {
            llSay(0, "Goodbye, " + name + "!");
        }
        else
        {
            llOwnerSay("Unknown: '" + clean + "'");
        }
    }

    state_entry()
    {
        llListen(0, "", NULL_KEY, "");
    }
}
String constants for llStringTrim
STRING_TRIM
Remove whitespace from both ends
STRING_TRIM_HEAD
Remove whitespace from the start only
STRING_TRIM_TAIL
Remove whitespace from the end only
Step 8

llParseString2List — Splitting Commands

llParseString2List splits a string into a list by one or more separator characters. This is the standard way to parse chat commands.

For example, if an avatar types /42 colour red, you split on spaces to get ["colour", "red"], then act on the parts.

LSLParsing a chat command
integer listenHandle;

default
{
    state_entry()
    {
        listenHandle = llListen(42, "", llGetOwner(), "");
        llOwnerSay("Ready. Try: /42 colour red");
    }

    listen(integer channel, string name, key id, string message)
    {
        // Split message on spaces; empty strings discarded
        list   parts   = llParseString2List(message, [" "], []);
        string command = llToLower(llList2String(parts, 0));

        if (command == "colour" && llGetListLength(parts) >= 2)
        {
            string colour = llToLower(llList2String(parts, 1));

            if (colour == "red")
                llSetColor(<1.0, 0.0, 0.0>, ALL_SIDES);
            else if (colour == "green")
                llSetColor(<0.0, 1.0, 0.0>, ALL_SIDES);
            else if (colour == "blue")
                llSetColor(<0.0, 0.0, 1.0>, ALL_SIDES);
            else
                llOwnerSay("Unknown colour: " + colour);
        }
        else
        {
            llOwnerSay("Usage: /42 colour [red|green|blue]");
        }
    }
}

You're now ready for Class 8 — the practical project where you'll combine everything you've learned into one complete, working script.