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.



Block Role Effect
Add row [Lizard]::custom
define Add row (name)
...
This custom block adds a row to the end of the 2-Dimensional list
Delete row [Snakes]::custom
define Delete row (name)
...
This custom block deletes a specified row
Insert row [Lizards] at [3]::custom
define Insert row (name) at (position)
...
This custom block inserts a row at the specified position
This custom block replaces a specified row and its data
Add [Goose] to [Birds]::custom
define Add (item) to (name)
...
This custom block adds an item to the end of a specified row
Delete [2] of [Cats]::custom
define Delete (position) of (name)
...
This custom block deletes an item specified by its position in a specified row
This custom block inserts an item at a position in a specified row
This custom block replaces an item in a specified row for another item
Cookies help us deliver our services. By using our services, you agree to our use of cookies.