tags: - arrays - Filter - Map - Reduce - Sort - array-operations
Arrays¶
Arrays are ordered collections of values. They can contain any type of data and provide powerful methods for manipulation and transformation.
Array Creation¶
Array Literal¶
numbers = [1, 2, 3, 4, 5];
names = ["Alice", "Bob", "Charlie"];
mixed = [1, "text", true, null, $Now()];
Empty Array¶
Using $Array Function¶
Nested Arrays¶
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
firstRow = matrix[0]; // [1, 2, 3]
firstElement = matrix[0][0]; // 1
Array Access¶
Zero-Based Indexing¶
items = ["first", "second", "third"];
first = items[0]; // "first"
second = items[1]; // "second"
third = items[2]; // "third"
Negative Indices Not Supported¶
items = [1, 2, 3, 4, 5];
// ERROR: Negative indices not supported
// last = items[-1];
// CORRECT: Calculate index
last = items[items.Length - 1]; // 5
Out of Bounds Access¶
Returns null:
Array Length¶
The .Length property returns the number of elements:
Modifying Arrays¶
Adding Elements¶
.Push() - Add to End¶
.Insert() - Add at Index¶
.AddRange() - Add Multiple¶
numbers = [1, 2, 3];
moreNumbers = [4, 5, 6];
result = numbers.AddRange(moreNumbers); // [1, 2, 3, 4, 5, 6]
Removing Elements¶
.Remove() - Remove by Value¶
.RemoveAt() - Remove by Index¶
.Clear() - Remove All¶
Updating Elements¶
items = [1, 2, 3];
// Direct assignment not supported
// items[1] = 20; // ERROR
// Use .RemoveAt() then .Insert()
items.RemoveAt(1);
items.Insert(1, 20); // [1, 20, 3]
Array Methods Overview¶
Arrays have 40+ methods. See Native Functions - Arrays for complete reference.
Filtering and Finding¶
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// .Filter() - Get all matching elements
evens = numbers.Filter((x) => x % 2 == 0); // [2, 4, 6, 8, 10]
// .Find() - Get first matching element
firstEven = numbers.Find((x) => x % 2 == 0); // 2
// .Any() - Check if any match
hasEvens = numbers.Any((x) => x % 2 == 0); // true
// All match: negate .Any() with the inverted condition
allPositive = !numbers.Any((x) => x <= 0); // true
Mapping and Transforming¶
numbers = [1, 2, 3, 4, 5];
// .Map() - Transform each element
doubled = numbers.Map((x) => x * 2); // [2, 4, 6, 8, 10]
// With index
indexed = numbers.Map((x, i) => x + i); // [1, 3, 5, 7, 9]
Reducing¶
numbers = [1, 2, 3, 4, 5];
// .ReduceToNum() - Reduce to number
sum = numbers.ReduceToNum((acc, x) => acc + x); // 15
// .ReduceToStr() - Reduce to string
text = numbers.ReduceToStr((acc, x) => acc + x.ToString()); // "12345"
// .ReduceToObj() - Reduce to object
counts = numbers.ReduceToObj((acc, x) => acc.AddProperty("n" + x, x * 2)); // { n1: 2, n2: 4, ... }
// Reduce methods take no initial value: ReduceToNum starts at 0, ReduceToStr at "", ReduceToObj at {}
Sorting¶
numbers = [5, 2, 8, 1, 9];
// .OrderBy() - Ascending
ascending = numbers.OrderBy((x) => x); // [1, 2, 5, 8, 9]
// .OrderByDescending() - Descending
descending = numbers.OrderByDescending((x) => x); // [9, 8, 5, 2, 1]
// Sort objects
customers = [
{ "name": "Charlie", "age": 35 },
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 }
];
byAge = customers.OrderBy((c) => c.age);
byName = customers.OrderBy((c) => c.name);
Taking and Skipping¶
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// .Take() - Get first N elements
first3 = numbers.Take(3); // [1, 2, 3]
// .Slice() - Skip first N elements
rest = numbers.Slice(3); // [4, 5, 6, 7, 8, 9, 10]
// Combine for pagination
page2 = numbers.Slice(5).Take(5); // [6, 7, 8, 9, 10]
Distinct Values¶
Iteration¶
names = ["Alice", "Bob", "Charlie"];
// .ForEach() - Execute for each element
names.ForEach((name) => $Log(name));
// With index
names.ForEach((name, index) => {
$Log(index + ": " + name);
});
Working with Objects in Arrays¶
Array of Objects¶
customers = [
{ "name": "Alice", "age": 30, "premium": 1200 },
{ "name": "Bob", "age": 25, "premium": 1500 },
{ "name": "Charlie", "age": 35, "premium": 1100 }
];
Filtering Objects¶
// Get customers with premium > 1200
highPremium = customers.Filter((c) => c.premium > 1200);
// Get active customers
active = customers.Filter((c) => c.isActive);
// Complex conditions
eligible = customers.Filter((c) => c.age >= 18 && c.premium < 2000);
Extracting Properties¶
// Get all names
names = customers.Map((c) => c.name);
// ["Alice", "Bob", "Charlie"]
// Get all ages
ages = customers.Map((c) => c.age);
// [30, 25, 35]
Transforming Objects¶
// Create simplified objects
simplified = customers.Map((c) => {
return {
"name": c.name,
"monthlyPremium": $Round(c.premium / 12, 2)
};
});
Aggregating Object Data¶
// Total premiums
totalPremiums = customers.ReduceToNum((acc, c) => acc + c.premium); // 3800
// Average age
totalAge = customers.ReduceToNum((acc, c) => acc + c.age);
avgAge = totalAge / customers.Length; // 30
// Concatenate names
allNames = customers.ReduceToStr((acc, c) => acc + c.name + ", "); // "Alice, Bob, Charlie, "
Sorting Objects¶
// Sort by age
byAge = customers.OrderBy((c) => c.age);
// Sort by premium (descending)
byPremium = customers.OrderByDescending((c) => c.premium);
// Sort by name
byName = customers.OrderBy((c) => c.name);
Chaining Array Methods¶
Combine multiple operations:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
result = numbers
.Filter((x) => x > 3) // [4, 5, 6, 7, 8, 9, 10]
.Map((x) => x * 2) // [8, 10, 12, 14, 16, 18, 20]
.Take(3); // [8, 10, 12]
Complex pipeline:
customers = getCustomers();
highValueActive = customers
.Filter((c) => c.isActive) // Active only
.Filter((c) => c.premium > 1000) // High premium
.Map((c) => {
// Transform
return {
"name": c.firstName + " " + c.lastName,
"annualPremium": c.premium * 12,
"yearsWithUs": $DifferenceInYears($Today(), c.joinDate)
};
})
.OrderByDescending((c) => c.annualPremium) // Sort by premium
.Take(10); // Top 10
Common Patterns¶
Pattern 1: Sum Array¶
numbers = [1, 2, 3, 4, 5];
// Using reduce
sum = numbers.ReduceToNum((acc, x) => acc + x);
// Using $Sum (if available)
sum = $Sum(numbers);
Pattern 2: Find Maximum¶
numbers = [5, 2, 8, 1, 9, 3];
// Using reduce
max = numbers.ReduceToNum((acc, x, i) => (i == 0 || x > acc ? x : acc));
// Using $Max
max = $Max(numbers);
Pattern 3: Count Matching¶
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Count even numbers
evenCount = numbers.Filter((x) => x % 2 == 0).Length;
// Or using reduce
evenCount = numbers.ReduceToNum((acc, x) => (x % 2 == 0 ? acc + 1 : acc));
Pattern 4: Group By¶
customers = getCustomers();
// Separate by age group
young = customers.Filter((c) => c.age < 25);
middle = customers.Filter((c) => c.age >= 25 && c.age < 65);
senior = customers.Filter((c) => c.age >= 65);
Pattern 5: Flatten Nested Arrays¶
nested = [
[1, 2],
[3, 4],
[5, 6]
];
// Manual flatten
flat = [];
nested.ForEach((arr) => flat.AddRange(arr));
// [1, 2, 3, 4, 5, 6]
Pattern 6: Remove Duplicates¶
Pattern 7: Pagination¶
items = getAllItems();
pageSize = 10;
pageNumber = 2; // Zero-based
page = items.Slice(pageNumber * pageSize).Take(pageSize);
Pattern 8: Convert to Object Map¶
customers = [
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" },
{ "id": 3, "name": "Charlie" }
];
// Create lookup object (manual)
lookup = {};
customers.ForEach((c) => {
$SetValue(lookup, c.id.ToString(), c);
});
// Now can access by id
customer2 = $GetValue(lookup, "2"); // { "id": 2, "name": "Bob" }
Nested Arrays¶
Creating Nested Structures¶
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
departments = [
{
"name": "Sales",
"employees": [
{ "name": "Alice", "salary": 50000 },
{ "name": "Bob", "salary": 55000 }
]
},
{
"name": "Engineering",
"employees": [
{ "name": "Charlie", "salary": 80000 },
{ "name": "Diana", "salary": 85000 }
]
}
];
Accessing Nested Data¶
// Access matrix element
value = matrix[1][2]; // 6
// Access nested employee
firstDept = departments[0];
firstEmployee = firstDept.employees[0];
salary = firstEmployee.salary; // 50000
// Or combined
salary = departments[0].employees[0].salary; // 50000
Flattening Nested Arrays¶
departments = [
{
"name": "Sales",
"employees": [{ "name": "Alice" }, { "name": "Bob" }]
},
{
"name": "Engineering",
"employees": [{ "name": "Charlie" }, { "name": "Diana" }]
}
];
// Flatten all employees
allEmployees = [];
departments.ForEach((dept) => allEmployees.AddRange(dept.employees));
// allEmployees = [
// { "name": "Alice" },
// { "name": "Bob" },
// { "name": "Charlie" },
// { "name": "Diana" }
// ]
Best Practices¶
1. Use Appropriate Method¶
// Less ideal: Map when you need filter
result = numbers.Map((x) => (x > 5 ? x : null)).Filter((x) => x != null);
// Better: Just filter
result = numbers.Filter((x) => x > 5);
2. Chain Operations Clearly¶
// Hard to read
result = customers
.Filter((c) => c.isActive)
.Map((c) => c.name)
.OrderBy((n) => n)
.Take(10);
// Easier to read
result = customers
.Filter((c) => c.isActive)
.Map((c) => c.name)
.OrderBy((n) => n)
.Take(10);
3. Check Length Before Access¶
4. Use .Any() Instead of .Length > 0¶
// Less clear
if (items.Filter((x) => x.isValid).Length > 0) {
// ...
}
// Clearer
if (items.Any((x) => x.isValid)) {
// ...
}
5. Prefer Reduce for Aggregation¶
// Less ideal: Manual loop
sum = 0;
index = 0;
while (index < numbers.Length) {
sum += numbers[index];
index++;
}
// Better: Use reduce
sum = numbers.ReduceToNum((acc, x) => acc + x);
6. Extract Complex Lambdas¶
// Complex inline lambda
result = items.Map((x) => {
// 20 lines of complex logic
});
// Better: Extract to function
function processItem(item) {
// 20 lines of complex logic
}
result = items.Map((item) => processItem(item));
Common Mistakes¶
Mistake 1: Expecting Negative Indices¶
items = [1, 2, 3, 4, 5];
// ERROR: Negative indices not supported
// last = items[-1];
// CORRECT
last = items[items.Length - 1];
Mistake 2: Direct Element Assignment¶
items = [1, 2, 3];
// ERROR: Cannot assign directly
// items[1] = 20;
// CORRECT: Remove and insert
items.RemoveAt(1);
items.Insert(1, 20);
Mistake 3: Forgetting Return in Reduce¶
numbers = [1, 2, 3, 4, 5];
// ERROR: Forgot to return accumulator
// sum = numbers.ReduceToNum((acc, x) => {
// acc + x; // Missing return!
// });
// CORRECT
sum = numbers.ReduceToNum((acc, x) => {
return acc + x;
});
// Or use expression body
sum = numbers.ReduceToNum((acc, x) => acc + x);
Mistake 4: Not Handling Empty Arrays¶
items = [];
// ERROR: Accessing empty array
// first = items[0]; // null
// CORRECT: Check first
if (items.Length > 0) {
first = items[0];
}
Mistake 5: Modifying Array During Iteration¶
// Problematic: Modifying while iterating
items.ForEach((item) => {
items.Push(item * 2); // Don't do this!
});
// Better: Create new array
doubled = items.Map((item) => item * 2);
Advanced Examples¶
Example 1: Complex Filtering¶
policies = getPolicies();
// Find high-value, active policies for senior customers
result = policies
.Filter((p) => p.isActive)
.Filter((p) => p.customer.age >= 65)
.Filter((p) => p.premium > 2000)
.OrderByDescending((p) => p.premium)
.Take(20);
Example 2: Statistical Aggregation¶
function calculateStats(numbers) {
count = numbers.Length;
sum = numbers.ReduceToNum((acc, x) => acc + x);
average = sum / count;
min = numbers.ReduceToNum((acc, x, i) => (i == 0 || x < acc ? x : acc));
max = numbers.ReduceToNum((acc, x, i) => (i == 0 || x > acc ? x : acc));
aboveAvg = numbers.Filter((x) => x > average).Length;
belowAvg = numbers.Filter((x) => x < average).Length;
return {
"count": count,
"sum": $Round(sum, 2),
"average": $Round(average, 2),
"min": min,
"max": max,
"aboveAverage": aboveAvg,
"belowAverage": belowAvg
};
}
sales = [1200, 1500, 980, 2100, 1750, 1300];
stats = calculateStats(sales);
Example 3: Data Transformation Pipeline¶
rawData = getCustomerData();
// Transform and enrich data
processedData = rawData
.Filter((c) => c.email != null) // Valid email
.Map((c) => {
age = $DifferenceInYears($Today(), c.birthDate);
return {
"customerId": c.id,
"fullName": c.firstName + " " + c.lastName,
"age": age,
"ageGroup": age < 25 ? "young" : (age < 65 ? "standard" : "senior"),
"email": c.email.ToLower(),
"tenure": $DifferenceInYears($Today(), c.joinDate),
"isHighValue": c.lifetimeValue > 50000
};
})
.Filter((c) => c.age >= 18) // Adults only
.OrderByDescending((c) => c.tenure); // Sort by tenure
Example 4: Grouped Aggregation¶
orders = getOrders();
// Group by status and calculate totals
pending = orders.Filter((o) => o.status == "pending");
completed = orders.Filter((o) => o.status == "completed");
cancelled = orders.Filter((o) => o.status == "cancelled");
report = {
"pending": {
"count": pending.Length,
"total": pending.ReduceToNum((acc, o) => acc + o.amount)
},
"completed": {
"count": completed.Length,
"total": completed.ReduceToNum((acc, o) => acc + o.amount)
},
"cancelled": {
"count": cancelled.Length,
"total": cancelled.ReduceToNum((acc, o) => acc + o.amount)
}
};
Example 5: Nested Array Processing¶
departments = getDepartments();
// Calculate company-wide salary statistics
allSalaries = [];
departments.ForEach((dept) => allSalaries.AddRange(dept.employees.Map((e) => e.salary)));
avgSalary =
allSalaries.ReduceToNum((acc, s) => acc + s) / allSalaries.Length;
highEarners = departments
.Map((dept) => {
return {
"department": dept.name,
"highEarners": dept.employees
.Filter((e) => e.salary > avgSalary)
.OrderByDescending((e) => e.salary)
};
})
.Filter((dept) => dept.highEarners.Length > 0);
Next Steps¶
- Explore all array methods: Native Functions - Arrays
- See practical examples: Array Operations
- Learn lambda expressions: Lambda Expressions
- Understand objects in arrays: Objects