Chapter 5.2: Improved Data Types - Arrays & Lists
Standard VScript arrays are functional, but primitive. They lack modern programming conveniences that you'd expect in any serious scripting environment. PCapture-Lib's Improved Data Types (IDT) module fixes this with two powerful classes: ArrayEx and List.
The Problem: Vanilla Arrays Are Limited
Consider this common scenario: you have an array of enemy health values and need to find which enemies have low health.
// Vanilla VScript: Manual iteration required
local healthValues = [100, 25, 80, 15, 90]
local lowHealthEnemies = []
foreach (health in healthValues) {
if (health < 30) {
lowHealthEnemies.append(health)
}
}
// Result: [25, 15]
This works, but it's verbose and requires manual loop management every time you need to filter data. What if you also need to find unique values? Remove duplicates? Reduce to a sum? Each requires writing another custom loop.
The Solution: ArrayEx
ArrayEx is a drop-in replacement for standard arrays that adds functional programming methods you'd expect from modern languages.
Creating an ArrayEx
// Method 1: Direct construction
local arr = ArrayEx(1, 2, 3, 4, 5)
// Method 2: From existing array
local vanillaArray = [10, 20, 30]
local arr2 = ArrayEx.FromArray(vanillaArray)
// Method 3: Pre-sized with default values
local arr3 = ArrayEx.WithSize(100, 0) // 100 zeros
Functional Methods: The Game Changers
Filter: Create a new array with only matching elements.
local healthValues = ArrayEx(100, 25, 80, 15, 90)
// Clean, declarative filtering
local lowHealth = healthValues.filter(function(idx, val) {
return val < 30
})
// Result: ArrayEx(25, 15)
Map: Transform each element.
local numbers = ArrayEx(1, 2, 3, 4, 5)
// Double each value
local doubled = numbers.map(function(val, idx) {
return val * 2
})
// Result: ArrayEx(2, 4, 6, 8, 10)
Reduce: Collapse to a single value.
local scores = ArrayEx(100, 50, 75, 200)
// Calculate total score
local total = scores.reduce(function(accumulator, currentValue) {
return accumulator + currentValue
}, 0)
// Result: 425
Unique: Remove duplicates.
local values = ArrayEx(1, 2, 2, 3, 4, 4, 5, 2)
local unique = values.unique()
// Result: ArrayEx(1, 2, 3, 4, 5)
Utility Methods
local arr = ArrayEx(10, 20, 30, 40, 50)
// Search for value or matching element
arr.search(30) // Returns: 2 (index)
arr.search(function(x) { return x > 35 }) // Returns: 3 (first match)
// Check if contains
arr.contains(20) // Returns: true
// Join to string
arr.join(", ") // Returns: "10, 20, 30, 40, 50"
// Safe get with default
arr.get(10, -1) // Returns: -1 (index 10 doesn't exist)
// Apply function in-place (modifies original)
arr.apply(function(val, idx) {
return val * 2
})
// arr is now: ArrayEx(20, 40, 60, 80, 100)
Lists: When Order and Insertion Matter
While ArrayEx is perfect for most use cases, there are scenarios where you need constant-time insertion/removal at arbitrary positions. This is where List (a doubly-linked list) excels.
When to Use a List
- Frequent insertions/deletions at the beginning or middle
- You need to maintain order but rarely access by index
- Building queues or stacks
- ❌ Don't use if you need fast random access by index (use ArrayEx instead)
Creating and Using a List
// Create a list
local myList = List(1, 2, 3, 4, 5)
// Or from array
local arr = [10, 20, 30]
local myList2 = List.FromArray(arr)
// Iterate (use .iter() for efficiency)
foreach (value in myList.iter()) {
printl(value)
}
// Insert at specific position
myList.insert(2, 99) // Insert 99 at index 2
// List is now: [1, 2, 99, 3, 4, 5]
// Remove by index
myList.remove(0) // Remove first element
// List is now: [2, 99, 3, 4, 5]
List Has the Same Functional Methods
local list = List(1, 2, 3, 4, 5)
// Filter, map, reduce work identically
local evens = list.filter(function(idx, val) { return val % 2 == 0 })
// Result: List(2, 4)
local doubled = list.map(function(val, idx) { return val * 2 })
// Result: List(2, 4, 6, 8, 10)
// Sort in-place (uses merge sort)
local unsorted = List(5, 2, 8, 1, 9)
unsorted.sort()
// Result: List(1, 2, 5, 8, 9)
⚠️ Memory Leak Warning: Lists use references between nodes. If you create circular references or don't properly clean up, the garbage collector may not free the memory. Always call .clear() when you're done with a list, or let it go out of scope naturally.
Performance Comparison
| Operation | Array | ArrayEx | List |
|---|---|---|---|
| Access by index | ⚡ O(1) | ⚡ O(1) | 🐌 O(n) |
| Insert at end | ⚡ O(1) | ⚡ O(1) | ⚡ O(1) |
| Insert at beginning | 🐌 O(n) | 🐌 O(n) | ⚡ O(1) |
| Filter/Map/Reduce | ❌ Manual | ✅ Built-in | ✅ Built-in |
Practical Example: Enemy Manager
// Manage a group of enemies with ArrayEx
EnemyManager <- class {
enemies = ArrayEx()
function AddEnemy(health, damage) {
enemies.append({ health = health, damage = damage })
}
function GetLowHealthEnemies() {
return enemies.filter(function(idx, enemy) {
return enemy.health < 30
})
}
function GetTotalDamage() {
return enemies.reduce(function(total, enemy) {
return total + enemy.damage
}, 0)
}
function HealAll(amount) {
enemies.apply(function(enemy, idx) {
enemy.health = min(100, enemy.health + amount)
return enemy
})
}
}
// Usage
EnemyManager.AddEnemy(100, 25)
EnemyManager.AddEnemy(20, 15)
EnemyManager.AddEnemy(75, 30)
local weak = EnemyManager.GetLowHealthEnemies()
printl("Weak enemies: " + weak.len()) // 1
local totalDamage = EnemyManager.GetTotalDamage()
printl("Total damage potential: " + totalDamage) // 70
EnemyManager.HealAll(10)
Conversion Between Types
local arr = ArrayEx(1, 2, 3, 4, 5)
// ArrayEx → List
local list = arr.tolist()
// List → ArrayEx
local backToArray = list.toarray()
// To vanilla array (for compatibility)
local vanillaArr = arr.arr
// To table (keys are array values, all values are null)
local table = arr.totable()
printl(2 in table) // true
Best Practices
- Use ArrayEx as your default collection type
- Use List only when you need frequent middle insertions
- Chain functional methods for readable data transformations
- Call
.clear()on Lists when done to avoid memory leaks - Remember: functional methods (filter/map) create new collections;
.apply()modifies in-place - ❌ Don't use List if you primarily access by index