| This article or section may not have content matching Scratch Wiki editing standards. Please improve it according to Scratch Wiki:Guidelines and Scratch Wiki:Editing Conventions. (September 2025) |
| Please expand this article or section. You can help by adding more information if you are an editor. More information might be found in a section of the talk page. (July 2022) |
This tutorial explains how to make a two-dimensional list. A 2D list is a list whose contents are also lists. Those lists are said to be nested. Two-dimensional lists are not a feature included in Scratch, but it can be simulated with normal lists. Unlike a 2D array, 2D lists (specifically those nested) can be of different lengths.
3 List Method
See an example project here.
The following lists are used:
(elements::list)— stores all the items of the nested lists(pointers::list)— stores where each nested list starts in the elements list(lengths::list)— stores the length of each nested list, in parallel with the pointers list.
What makes this tricky is that as all items are stored in the elements list, inserting or deleting them will shift all items after. The pointers and lengths lists need to be updated to account for this.
This tutorial will implement lists that are indexed from 1, like Scratch's lists.
Custom blocks will be used for readability and maintainability. Return values will be stored in a variable named "return". Arguments for the indices of the list and nested list will be called "index0" and "index1", respectively.
Adding a Nested List
define add empty list add ((length of [elements v]) + (1)) to [pointers v] add [0] to [lengths v]
Get Length of Nested List
define get length of list (index0) set [return v] to (item (index0) of [lengths v])
Get Item
define get item at (index0) (index1) if <(item (index0) of [lengths v]) and <<(index1) > [0]> and <not <(index1) > (item (index0) of [lengths v])>>>> then // check if indices are in bounds set [return v] to (item (((item (index0) of [pointers v]) + (index1)) - (1)) of [elements v]) else set [return v] to [] // out of bounds, return empty string end
Replace Item
define replace item at (index0) (index1) with (value) if <(item (index0) of [lengths v]) and <<(index1) > [0]> and <not <(index1) > (item (index0) of [lengths v])>>>> then // bounds check replace item (((item (index0) of [pointers v]) + (index1)) - (1)) of [elements v] with (value) end
Add Item
define add (value) to list (index0)
if <(item (index0) of [pointers v]) and <(length of [elements v]::data) < [200000]>> then // bounds check
insert (value) at ((item (index0) of [pointers v]) + (item (index0) of [lengths v])) of [elements v]
replace item (index0) of [lengths v] with ((item (index0) of [lengths v]) + (1))
set [list i v] to (index0)
repeat ((length of [pointers v]::data) - (index0)) // update pointers
change [i v] by (1)
replace item (list i) of [pointers v] with ((item (list i) of [pointers v]) + (1)) // increase by 1 item
end
end
String Encoding Method
This method uses data serialization to store the nested lists as strings, which are than placed in another list.
For this method, you will need the lists:
(Storage::list)— stores the strings of lists(Compress::list)— helps compress and decompress the lists(Names::list)— stores the names of the rows
You will also need the variables:
(Character) (Index) (Item) (string)
To start, data serialization scripts will need to be made. We will need 2 define blocks:
define Compile (item) ... define Decompile ...
A script to convert items into 1 string will also be nessersary
define Compile (item) set [Index v] to (1) repeat (length of (item)) set [Character v] to (letter (Index) of (item)) if <[|~] contains(Character)?> then set [String v] to (join (String) [~] end set [String v] to (join (String) (Character) change [Index v] by (1) end set [String v] to (join (String) [|]
Next, a script to decompile the string into the items is needed to be made
define Decompile set [Item v] to () forever set [Character v] to (letter (Index) of (String) change [Index v] by (1) if <[|] contains (Character)?> then stop [this script v] end if<[~] contains (Character)?> then set [Character v] to (letter (Index) of (String)) change [Index v] by (1) end set [Item v] to (join (Item)(Character))
With the data serialization scripts completed, we will now need a way to read and write the compressed rows to turn them into lists.
define Read (row)//The input 'row' is to be the name of the row delete all of [Compress v] set [String v] to (item (item # of (row) in [Names v]) of [Store v]) set [Index v] to (1) Decompile::custom repeat until <(item) = ()> add (item) to [Compress v] Decompile::custom define Write (row)//The input 'row' is to be the name of the row set [string v] to () repeat (length of [Compress v]) Compile (item (1) of [Compress v])::custom delete (1) of [Compress v] end replace item (item # of (row) in [Names v]) of [Store v] with (String)
With the data serilisation, and read and write scripts done, we will now focus on initiating the 2D list. To start we will reset the lists:
when gf clicked delete all of [Store v] delete all of [Compress v] delete all of [Names v]
Next we'll create a custom block to add, delete, insert, and replace rows in the 2D list
define Add row (name) add (name) to [Names v] add () to [Store v] define Delete row (name) delete (name) of [Names v] delete (name) of [Store v] define Insert row (name) at (position) insert (name) at (position) of [Names v] insert () at (position) of [Store v] define Replace row (oldname) with row (name) and data (data) replace item (item # of (oldname) in [Names v]) of [Names v] with (name) replace item (item # of (name) in [Names v]) of [Store v] with (data)
With that done, we now create the custom blocks to edit the rows themselves.
define Add (item) to (name) Read (name)::custom add (item) to [Compress v] Write (name)::custom define Delete (item#) of (name) Read (name)::custom delete (position) of [Compress v] Write (name)::custom define Insert (item) at (position) of (name) Read (name)::custom insert (item) at (position) of [Compress v] Write (name)::custom //vvvvv more down vvvvv define Replace item (position) of (name) with (item) Read (name)::custom replace item (position) of [Compress v] with (item) Write (name)::custom
Now, all of these custom blocks mean nothing if you don't know how to use them. The following table will list the custom block, function of the block, and an effect of the block. The effects will be based on this 2D list.











