# Orlando JavaScript/TypeScript API Documentation
Complete API reference for using Orlando transducers in JavaScript and TypeScript applications.
## Installation
```bash
npm install orlando-transducers
```
Or use directly from a CDN:
```html
<script type="module">
import init, { Pipeline } from './pkg/orlando.js';
await init();
</script>
```
## Quick Start
```javascript
import init, { Pipeline } from 'orlando-transducers';
// Initialize WASM module
await init();
// Create a pipeline
const pipeline = new Pipeline()
.map(x => x * 2)
.filter(x => x > 10)
.take(5);
// Execute on data
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = pipeline.toArray(data);
console.log(result); // [12, 14, 16, 18, 20]
```
## Core Concepts
### Transducers vs Array Methods
**Traditional approach** (creates intermediate arrays):
```javascript
const result = data
.map(x => x * 2) // creates intermediate array
.filter(x => x > 10) // creates another intermediate array
.slice(0, 5); // final result
```
**Orlando approach** (single pass, no intermediates):
```javascript
const pipeline = new Pipeline()
.map(x => x * 2)
.filter(x => x > 10)
.take(5);
const result = pipeline.toArray(data); // single pass!
```
### Performance Benefits
1. **No intermediate allocations** - Processes data in a single pass
2. **Early termination** - Stops processing as soon as possible
3. **Composable** - Build complex pipelines from simple operations
4. **WASM-powered** - Native performance via WebAssembly
## API Reference
### Pipeline Class
The main entry point for building transducer pipelines.
#### Constructor
```typescript
new Pipeline(): Pipeline
```
Creates a new empty pipeline.
```javascript
const pipeline = new Pipeline();
```
---
### Transformation Methods
All transformation methods return a new `Pipeline` instance, allowing for method chaining.
#### `map(fn)`
Transforms each value using the provided function.
```typescript
map(fn: (value: T) => U): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline()
.map(x => x * 2)
.map(x => x + 1);
pipeline.toArray([1, 2, 3]); // [3, 5, 7]
```
**Use cases:**
- Data transformation
- Property extraction
- Type conversion
- Calculations
---
#### `filter(predicate)`
Keeps only values that match the predicate.
```typescript
filter(predicate: (value: T) => boolean): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline()
.filter(x => x % 2 === 0)
.filter(x => x > 10);
pipeline.toArray([1, 5, 12, 20, 3]); // [12, 20]
```
**Use cases:**
- Filtering data
- Validation
- Conditional inclusion
---
#### `take(n)`
Takes the first `n` elements, then stops processing.
```typescript
take(n: number): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline()
.filter(x => x % 2 === 0)
.take(3);
// Only processes until 3 evens are found!
pipeline.toArray([1, 2, 3, 4, 5, 6, 7, 8]); // [2, 4, 6]
```
**Use cases:**
- Pagination
- Limiting results
- Top-N queries
- Early termination for performance
**Performance note:** This is where Orlando shines! It stops processing the moment it has enough elements.
---
#### `takeWhile(predicate)`
Takes elements while the predicate is true, then stops.
```typescript
takeWhile(predicate: (value: T) => boolean): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline()
.takeWhile(x => x < 100);
pipeline.toArray([1, 5, 50, 200, 10]); // [1, 5, 50]
```
**Use cases:**
- Taking until a condition
- Reading until delimiter
- Streaming data processing
---
#### `drop(n)`
Skips the first `n` elements.
```typescript
drop(n: number): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline()
.drop(3);
pipeline.toArray([1, 2, 3, 4, 5]); // [4, 5]
```
**Use cases:**
- Pagination (skip)
- Removing headers
- Offset-based queries
---
#### `dropWhile(predicate)`
Skips elements while the predicate is true.
```typescript
dropWhile(predicate: (value: T) => boolean): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline()
.dropWhile(x => x < 10);
pipeline.toArray([1, 5, 12, 20, 3]); // [12, 20, 3]
```
**Use cases:**
- Skipping headers
- Removing prefixes
- Starting from a condition
---
#### `tap(fn)`
Performs side effects without modifying values.
```typescript
tap(fn: (value: T) => void): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline()
.tap(x => console.log('Processing:', x))
.map(x => x * 2)
.tap(x => console.log('Result:', x));
pipeline.toArray([1, 2, 3]);
// Logs:
// Processing: 1
// Result: 2
// Processing: 2
// Result: 4
// Processing: 3
// Result: 6
```
**Use cases:**
- Debugging
- Logging
- Analytics
- Progress tracking
---
### Terminal Operations (Collectors)
Terminal operations execute the pipeline and return a result.
#### `toArray(source)`
Collects all results into an array.
```typescript
toArray(source: Array<T>): Array<U>
```
**Example:**
```javascript
const pipeline = new Pipeline()
.map(x => x * 2);
const result = pipeline.toArray([1, 2, 3]); // [2, 4, 6]
```
---
#### `reduce(source, reducer, initial)`
Custom reduction with a reducer function.
```typescript
reduce(source: Array<T>,
reducer: (acc: A, value: U) => A,
initial: A): A
```
**Example:**
```javascript
const pipeline = new Pipeline()
.map(x => x * 2);
const sum = pipeline.reduce(
[1, 2, 3, 4],
(acc, x) => acc + x,
0
);
console.log(sum); // 20
```
**Use cases:**
- Custom aggregations
- Building objects from arrays
- Complex reductions
---
### Multi-Input Operations
These standalone functions work with multiple arrays. They don't use the Pipeline API.
#### `takeLast(array, n)`
Takes the last N elements from an array.
```typescript
takeLast(source: Array<T>, n: number): Array<T>
```
**Example:**
```javascript
import { takeLast } from 'orlando-transducers';
const result = takeLast([1, 2, 3, 4, 5], 3);
// result: [3, 4, 5]
```
**Use cases:**
- Get recent items (last N logs, events, etc.)
- Tail of a sequence
- "Show more" from end
**Note:** Unlike `take()`, this requires processing the entire array since it needs to know which elements are last.
---
#### `dropLast(array, n)`
Drops the last N elements from an array.
```typescript
dropLast(source: Array<T>, n: number): Array<T>
```
**Example:**
```javascript
import { dropLast } from 'orlando-transducers';
const result = dropLast([1, 2, 3, 4, 5], 2);
// result: [1, 2, 3]
```
**Use cases:**
- Remove trailing elements
- Trim recent history
- Keep all except last N
---
#### `aperture(array, size)`
Creates sliding windows of a given size.
```typescript
aperture(source: Array<T>, size: number): Array<Array<T>>
```
**Example:**
```javascript
import { aperture } from 'orlando-transducers';
const data = [1, 2, 3, 4, 5];
const windows = aperture(data, 3);
// windows: [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
```
**Use cases:**
- Moving averages
- N-gram analysis
- Sliding window algorithms
- Comparing adjacent elements
**Example - Moving average:**
```javascript
const numbers = [10, 20, 30, 40, 50];
const windows = aperture(numbers, 3);
const averages = windows.map(w => w.reduce((a, b) => a + b) / w.length);
// averages: [20, 30, 40]
```
---
#### `merge(arrays)`
Interleaves elements from multiple arrays in round-robin fashion.
```typescript
merge(arrays: Array<Array<T>>): Array<T>
```
**Example:**
```javascript
import { merge } from 'orlando-transducers';
const a = [1, 2, 3];
const b = [4, 5, 6];
const result = merge([a, b]);
// result: [1, 4, 2, 5, 3, 6]
```
**Use cases:**
- Interleaving data streams
- Round-robin scheduling
- Combining event logs chronologically
---
#### `intersection(arrayA, arrayB)`
Returns elements that appear in both arrays.
```typescript
intersection(a: Array<T>, b: Array<T>): Array<T>
```
**Example:**
```javascript
import { intersection } from 'orlando-transducers';
const a = [1, 2, 3, 4];
const b = [3, 4, 5, 6];
const common = intersection(a, b);
// common: [3, 4]
```
**Use cases:**
- Finding common elements
- Set operations
- Filtering by membership
---
#### `difference(arrayA, arrayB)`
Returns elements in A that are not in B.
```typescript
difference(a: Array<T>, b: Array<T>): Array<T>
```
**Example:**
```javascript
import { difference } from 'orlando-transducers';
const a = [1, 2, 3, 4];
const b = [3, 4, 5, 6];
const uniqueToA = difference(a, b);
// uniqueToA: [1, 2]
```
**Use cases:**
- Finding new/removed items
- Exclusion lists
- Diff operations
---
#### `union(arrayA, arrayB)`
Returns all unique elements from both arrays.
```typescript
union(a: Array<T>, b: Array<T>): Array<T>
```
**Example:**
```javascript
import { union } from 'orlando-transducers';
const a = [1, 2, 3];
const b = [3, 4, 5];
const allUnique = union(a, b);
// allUnique: [1, 2, 3, 4, 5]
```
**Use cases:**
- Combining datasets
- Merging unique items
- Set union
---
#### `symmetricDifference(arrayA, arrayB)`
Returns elements in either array but not both.
```typescript
symmetricDifference(a: Array<T>, b: Array<T>): Array<T>
```
**Example:**
```javascript
import { symmetricDifference } from 'orlando-transducers';
const a = [1, 2, 3, 4];
const b = [3, 4, 5, 6];
const unique = symmetricDifference(a, b);
// unique: [1, 2, 5, 6]
```
**Use cases:**
- Finding differences
- XOR operations
- Change detection
---
### Logic Functions (Phase 3)
Predicate combinators for cleaner conditional logic.
#### `both(pred1, pred2)`
Combines two predicates with AND logic.
```typescript
both(p1: (value: T) => boolean, p2: (value: T) => boolean): (value: T) => boolean
```
**Example:**
```javascript
import { both } from 'orlando-transducers';
const isPositive = x => x > 0;
const isEven = x => x % 2 === 0;
const isPositiveEven = both(isPositive, isEven);
const result = [1, 2, 3, 4, -2].filter(isPositiveEven);
// result: [2, 4]
```
---
#### `either(pred1, pred2)`
Combines two predicates with OR logic.
```typescript
either(p1: (value: T) => boolean, p2: (value: T) => boolean): (value: T) => boolean
```
**Example:**
```javascript
import { either } from 'orlando-transducers';
const isSmall = x => x < 10;
const isLarge = x => x > 100;
const isExtreme = either(isSmall, isLarge);
const result = [5, 50, 105].filter(isExtreme);
// result: [5, 105]
```
---
#### `complement(predicate)`
Negates a predicate.
```typescript
complement(pred: (value: T) => boolean): (value: T) => boolean
```
**Example:**
```javascript
import { complement } from 'orlando-transducers';
const isEven = x => x % 2 === 0;
const isOdd = complement(isEven);
const result = [1, 2, 3, 4, 5].filter(isOdd);
// result: [1, 3, 5]
```
---
#### `allPass(predicates)`
Returns true if ALL predicates pass.
```typescript
allPass(predicates: Array<(value: T) => boolean>): (value: T) => boolean
```
**Example:**
```javascript
import { allPass } from 'orlando-transducers';
const isValid = allPass([
user => user.age >= 18,
user => user.email.includes('@'),
user => user.verified
]);
const validUsers = users.filter(isValid);
```
---
#### `anyPass(predicates)`
Returns true if ANY predicate passes.
```typescript
anyPass(predicates: Array<(value: T) => boolean>): (value: T) => boolean
```
**Example:**
```javascript
import { anyPass } from 'orlando-transducers';
const hasDiscount = anyPass([
user => user.isPremium,
user => user.isStudent,
user => user.couponCode
]);
const discountedUsers = users.filter(hasDiscount);
```
---
## Common Patterns
### Pagination
```javascript
function paginate(data, page, pageSize) {
return new Pipeline()
.drop(page * pageSize)
.take(pageSize)
.toArray(data);
}
const page2 = paginate([1,2,3,4,5,6,7,8,9,10], 1, 3);
// [4, 5, 6]
```
### Data Transformation Pipeline
```javascript
const processUsers = new Pipeline()
.filter(user => user.active)
.map(user => ({
id: user.id,
name: user.fullName,
email: user.email.toLowerCase()
}))
.filter(user => user.email.endsWith('@company.com'))
.take(100);
const activeCompanyUsers = processUsers.toArray(users);
```
### Find First Matching
```javascript
const findFirst = new Pipeline()
.filter(x => x > 100)
.take(1);
const result = findFirst.toArray(data);
const firstMatch = result[0]; // or undefined
```
### Debugging Pipeline
```javascript
const debugPipeline = new Pipeline()
.tap(x => console.log('Input:', x))
.map(x => x * 2)
.tap(x => console.log('After map:', x))
.filter(x => x > 10)
.tap(x => console.log('After filter:', x));
```
### Combining Multiple Operations
```javascript
const complexPipeline = new Pipeline()
.map(x => x.trim()) // clean whitespace
.filter(x => x.length > 0) // remove empty
.map(x => x.toLowerCase()) // normalize
.filter(x => !x.startsWith('#')) // remove comments
.map(x => x.split('=')) // parse key=value
.filter(([k, v]) => k && v) // validate pairs
.map(([k, v]) => ({ [k]: v })); // to objects
const config = complexPipeline.toArray(lines);
```
## TypeScript Support
Orlando automatically generates TypeScript definitions. Import with full type safety:
```typescript
import init, { Pipeline } from 'orlando-transducers';
await init();
interface User {
id: number;
name: string;
email: string;
active: boolean;
}
interface UserDTO {
id: number;
displayName: string;
}
const pipeline = new Pipeline()
.filter((user: User) => user.active)
.map((user: User): UserDTO => ({
id: user.id,
displayName: user.name
}))
.take(10);
const users: User[] = [/* ... */];
const dtos: UserDTO[] = pipeline.toArray(users);
```
## Performance Tips
### 1. Use Early Termination
```javascript
// ❌ Processes all 1 million items
const bad = data
.map(expensiveOperation)
.slice(0, 10);
// ✅ Stops after 10 items
const good = new Pipeline()
.map(expensiveOperation)
.take(10)
.toArray(data);
```
### 2. Filter Early
```javascript
// ❌ Maps all items, then filters
const bad = new Pipeline()
.map(expensiveOperation)
.filter(x => x.isValid);
// ✅ Filters first, then maps fewer items
const good = new Pipeline()
.filter(x => x.isValid)
.map(expensiveOperation);
```
### 3. Reuse Pipelines
```javascript
// Define once
const userProcessor = new Pipeline()
.filter(user => user.active)
.map(user => user.email);
// Reuse multiple times
const emails1 = userProcessor.toArray(users1);
const emails2 = userProcessor.toArray(users2);
```
### 4. Avoid Unnecessary Operations
```javascript
// ❌ Multiple passes
const bad = data
.map(x => x * 2)
.map(x => x + 1);
// ✅ Single pass
const good = new Pipeline()
.map(x => (x * 2) + 1)
.toArray(data);
```
## Browser Compatibility
Orlando uses WebAssembly and works in all modern browsers:
- ✅ Chrome 57+
- ✅ Firefox 52+
- ✅ Safari 11+
- ✅ Edge 16+
For older browsers, include a WASM polyfill.
## Error Handling
```javascript
try {
await init(); // Initialize WASM
const pipeline = new Pipeline()
.map(x => {
if (typeof x !== 'number') {
throw new Error(`Expected number, got ${typeof x}`);
}
return x * 2;
});
const result = pipeline.toArray(data);
} catch (error) {
console.error('Pipeline error:', error);
}
```
## Examples Repository
See the `/examples` directory for complete working examples:
- `examples/basic.html` - Basic usage
- `examples/pagination.html` - Pagination example
- `examples/data-processing.html` - Real-world data processing
- `examples/performance.html` - Performance comparison
- `examples/typescript/` - TypeScript examples
## Multi-Input Operations
Orlando provides powerful multi-input operations for combining and comparing arrays. These are standalone functions (not Pipeline methods) that enable hybrid composition patterns.
### `merge(arrays)`
Merges multiple arrays by interleaving their elements in round-robin fashion.
```typescript
merge(arrays: Array<Array<T>>): Array<T>
```
**Example:**
```javascript
import { merge } from 'orlando-transducers';
const a = [1, 2, 3];
const b = [4, 5, 6];
const c = [7, 8, 9];
const result = merge([a, b, c]);
// result: [1, 4, 7, 2, 5, 8, 3, 6, 9]
```
**Handles different lengths:**
```javascript
const a = [1, 2];
const b = [3, 4, 5, 6];
const result = merge([a, b]);
// result: [1, 3, 2, 4, 5, 6]
```
**Use cases:**
- Round-robin scheduling
- Interleaving data from multiple sources
- Creating alternating patterns
**Hybrid Composition Example:**
```javascript
// Process each stream differently, then merge
const pipeline1 = new Pipeline().map(x => x * 2);
const pipeline2 = new Pipeline().map(x => x + 10);
const stream1 = pipeline1.toArray([1, 2, 3]);
const stream2 = pipeline2.toArray([1, 2, 3]);
const merged = merge([stream1, stream2]);
// merged: [2, 11, 4, 12, 6, 13]
```
---
### `intersection(arrayA, arrayB)`
Returns elements that appear in both arrays.
```typescript
intersection(arrayA: Array<T>, arrayB: Array<T>): Array<T>
```
**Example:**
```javascript
import { intersection } from 'orlando-transducers';
const a = [1, 2, 3, 4, 5];
const b = [3, 4, 5, 6, 7];
const common = intersection(a, b);
// common: [3, 4, 5]
```
**Preserves order from first array:**
```javascript
const a = [5, 3, 4, 1];
const b = [1, 3, 5];
const result = intersection(a, b);
// result: [5, 3, 1] (order from a)
```
**Use cases:**
- Finding matching records across datasets
- Filtering by membership
- Database-style joins
---
### `difference(arrayA, arrayB)`
Returns elements in the first array but not in the second.
```typescript
difference(arrayA: Array<T>, arrayB: Array<T>): Array<T>
```
**Example:**
```javascript
import { difference } from 'orlando-transducers';
const a = [1, 2, 3, 4, 5];
const b = [3, 4, 5, 6, 7];
const uniqueToA = difference(a, b);
// uniqueToA: [1, 2]
```
**Use cases:**
- Finding new/deleted items
- Exclusion lists
- Data reconciliation
---
### `union(arrayA, arrayB)`
Returns all unique elements from both arrays.
```typescript
union(arrayA: Array<T>, arrayB: Array<T>): Array<T>
```
**Example:**
```javascript
import { union } from 'orlando-transducers';
const a = [1, 2, 3];
const b = [3, 4, 5];
const allUnique = union(a, b);
// allUnique: [1, 2, 3, 4, 5]
```
**Removes duplicates:**
```javascript
const a = [1, 2, 2, 3];
const b = [3, 4, 4, 5];
const result = union(a, b);
// result: [1, 2, 3, 4, 5]
```
**Use cases:**
- Combining datasets without duplicates
- Creating master lists
- Deduplication across sources
---
### `symmetricDifference(arrayA, arrayB)`
Returns elements that appear in exactly one array (not both).
```typescript
symmetricDifference(arrayA: Array<T>, arrayB: Array<T>): Array<T>
```
**Example:**
```javascript
import { symmetricDifference } from 'orlando-transducers';
const a = [1, 2, 3, 4];
const b = [3, 4, 5, 6];
const uniqueToEach = symmetricDifference(a, b);
// uniqueToEach: [1, 2, 5, 6]
```
**No overlap:**
```javascript
const a = [1, 2];
const b = [3, 4];
const result = symmetricDifference(a, b);
// result: [1, 2, 3, 4]
```
**Use cases:**
- Finding changed items
- XOR operations
- Detecting differences between versions
---
## Hybrid Composition Patterns
Combine transducers with multi-input operations for maximum flexibility.
### Pattern 1: Process → Combine
Process streams independently, then combine:
```javascript
const pipeline = new Pipeline()
.filter(x => x > 0)
.map(x => x * 2);
const stream1 = pipeline.toArray(data1);
const stream2 = pipeline.toArray(data2);
const combined = intersection(stream1, stream2);
```
### Pattern 2: Combine → Process
Combine first, then process:
```javascript
const merged = merge([data1, data2, data3]);
const pipeline = new Pipeline()
.filter(x => x % 2 === 0)
.take(10);
const result = pipeline.toArray(merged);
```
### Real-World Example: Finding Common Active Users
```javascript
// Get active users from both datasets
const activeInA = new Pipeline()
.filter(user => user.active)
.map(user => user.id)
.toArray(usersA);
const activeInB = new Pipeline()
.filter(user => user.active)
.map(user => user.id)
.toArray(usersB);
// Find users active in both systems
const activeInBoth = intersection(activeInA, activeInB);
```
For more patterns and examples, see the [Hybrid Composition Guide](../HYBRID_COMPOSITION.md).
---
## Advanced Collectors
Orlando provides specialized collector functions for complex data analysis and aggregation.
### `frequencies(array)`
Counts occurrences of each element in the array.
```typescript
frequencies(array: Array<T>): Map<T, number>
```
**Example:**
```javascript
import { frequencies } from 'orlando-transducers';
const data = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'];
const counts = frequencies(data);
// counts: Map {
// 'apple' => 3,
// 'banana' => 2,
// 'cherry' => 1
// }
```
**With pipeline:**
```javascript
const pipeline = new Pipeline()
.filter(word => word.length > 5)
.map(word => word.toLowerCase());
const words = pipeline.toArray(text.split(' '));
const wordCounts = frequencies(words);
```
**Use cases:**
- Word frequency analysis
- Event counting
- Distribution analysis
- Histogram generation
---
### `partitionBy(array, keyFn)`
Splits array into consecutive groups where keyFn returns the same value.
```typescript
partitionBy<T, K>(array: Array<T>, keyFn: (value: T) => K): Array<Array<T>>
```
**Example:**
```javascript
import { partitionBy } from 'orlando-transducers';
const numbers = [1, 1, 2, 3, 3, 3, 4, 5, 5];
const groups = partitionBy(numbers, x => x);
// groups: [[1, 1], [2], [3, 3, 3], [4], [5, 5]]
const data = [
{ type: 'A', value: 1 },
{ type: 'A', value: 2 },
{ type: 'B', value: 3 },
{ type: 'B', value: 4 }
];
const byType = partitionBy(data, item => item.type);
// byType: [[{type:'A', value:1}, {type:'A', value:2}],
// [{type:'B', value:3}, {type:'B', value:4}]]
```
**Use cases:**
- Grouping consecutive similar items
- Run-length encoding
- Chunking by property changes
- Log file analysis
---
### `topK(array, k, [compareFn])`
Returns the k largest elements (maintains relative order).
```typescript
topK<T>(array: Array<T>, k: number, compareFn?: (a: T, b: T) => number): Array<T>
```
**Example:**
```javascript
import { topK } from 'orlando-transducers';
const scores = [85, 92, 78, 95, 88, 72, 99, 81];
const topThree = topK(scores, 3);
// topThree: [99, 95, 92]
// Custom comparison
const users = [
{ name: 'Alice', score: 85 },
{ name: 'Bob', score: 92 },
{ name: 'Charlie', score: 88 }
];
const topUsers = topK(users, 2, (a, b) => a.score - b.score);
// topUsers: [{name: 'Bob', score: 92}, {name: 'Charlie', score: 88}]
```
**Use cases:**
- Leaderboards
- Top performers
- High scores
- Best matches
---
### `reservoirSample(array, k)`
Random sampling with uniform probability (reservoir sampling algorithm).
```typescript
reservoirSample<T>(array: Array<T>, k: number): Array<T>
```
**Example:**
```javascript
import { reservoirSample } from 'orlando-transducers';
const largeDataset = Array.from({ length: 10000 }, (_, i) => i);
const sample = reservoirSample(largeDataset, 100);
// sample: 100 randomly selected items with uniform probability
```
**Use cases:**
- Statistical sampling
- Random selection from large datasets
- A/B testing
- Data subset creation
---
### `cartesianProduct(arrayA, arrayB)`
Returns all possible pairs from two arrays.
```typescript
cartesianProduct<T, U>(arrayA: Array<T>, arrayB: Array<U>): Array<[T, U]>
```
**Example:**
```javascript
import { cartesianProduct } from 'orlando-transducers';
const colors = ['red', 'blue'];
const sizes = ['S', 'M', 'L'];
const combinations = cartesianProduct(colors, sizes);
// combinations: [
// ['red', 'S'], ['red', 'M'], ['red', 'L'],
// ['blue', 'S'], ['blue', 'M'], ['blue', 'L']
// ]
```
**Use cases:**
- Product variant generation
- Combinatorial analysis
- Test case generation
- Grid coordinates
---
### `zipLongest(arrayA, arrayB, [fillValue])`
Like zip, but continues until the longer array is exhausted, filling missing values.
```typescript
zipLongest<T, U>(arrayA: Array<T>, arrayB: Array<U>, fillValue?: any): Array<[T | any, U | any]>
```
**Example:**
```javascript
import { zipLongest } from 'orlando-transducers';
const a = [1, 2, 3];
const b = ['a', 'b'];
const result = zipLongest(a, b, null);
// result: [[1, 'a'], [2, 'b'], [3, null]]
const result2 = zipLongest(a, b, undefined);
// result2: [[1, 'a'], [2, 'b'], [3, undefined]]
```
**Use cases:**
- Handling arrays of different lengths
- Data alignment
- Missing value handling
- Table formatting
---
## Statistical Operations
Orlando provides efficient statistical analysis operations for numeric data.
### `product(array)`
Multiplies all numbers in an array.
```typescript
product(array: Array<number>): number
```
**Example:**
```javascript
import { product } from 'orlando-transducers';
const numbers = [2, 3, 4];
const result = product(numbers);
// result: 24
const pipeline = new Pipeline().filter(x => x > 0);
const filtered = pipeline.toArray([1, -2, 3, 4]);
const prod = product(filtered);
// prod: 12
```
**Use cases:**
- Mathematical calculations
- Compound growth rates
- Probability calculations
---
### `mean(array)`
Calculates the arithmetic mean (average) of an array.
```typescript
**Example:**
```javascript
import { mean } from 'orlando-transducers';
const scores = [85, 92, 78, 95, 88];
const average = mean(scores);
// average: 87.6
// Returns undefined for empty arrays
mean([]); // undefined
```
**Use cases:**
- Performance metrics
- Grade calculations
- Statistical analysis
---
### `median(array)`
Finds the median (middle value) of an array.
```typescript
**Example:**
```javascript
import { median } from 'orlando-transducers';
const odd = [1, 3, 5, 7, 9];
median(odd); // 5
const even = [1, 2, 3, 4];
median(even); // 2.5
median([]); // undefined
```
**Use cases:**
- Robust averaging (less affected by outliers)
- Salary distributions
- Performance baselines
---
### `variance(array)`
Calculates the sample variance.
```typescript
**Example:**
```javascript
import { variance } from 'orlando-transducers';
const data = [2, 4, 6, 8, 10];
const v = variance(data);
// v: 10.0
variance([5]); // undefined (need at least 2 values)
```
**Use cases:**
- Measuring data spread
- Quality control
- Risk assessment
---
### `stdDev(array)`
Calculates the standard deviation (square root of variance).
```typescript
**Example:**
```javascript
import { stdDev } from 'orlando-transducers';
const data = [2, 4, 6, 8, 10];
const sd = stdDev(data);
// sd: 3.16...
// Useful for measuring consistency
const player1Scores = [50, 52, 48, 51, 49];
const player2Scores = [30, 70, 20, 80, 10];
stdDev(player1Scores); // ~1.58 (consistent)
stdDev(player2Scores); // ~27.39 (variable)
```
**Use cases:**
- Data consistency measurement
- Outlier detection
- Statistical analysis
---
### `min(array)` / `max(array)`
Finds the minimum or maximum value.
```typescript
```
**Example:**
```javascript
import { min, max } from 'orlando-transducers';
const scores = [85, 92, 78, 95, 88];
min(scores); // 78
max(scores); // 95
min([]); // undefined
```
---
### `minBy(array, keyFn)` / `maxBy(array, keyFn)`
Finds the element with minimum or maximum key value.
```typescript
```
**Example:**
```javascript
import { minBy, maxBy } from 'orlando-transducers';
const users = [
{ name: 'Alice', score: 85 },
{ name: 'Bob', score: 92 },
{ name: 'Charlie', score: 78 }
];
const lowest = minBy(users, u => u.score);
// lowest: { name: 'Charlie', score: 78 }
const highest = maxBy(users, u => u.score);
// highest: { name: 'Bob', score: 92 }
```
**Use cases:**
- Finding extremes in object arrays
- Best/worst performer
- Price comparisons
---
### `quantile(array, p)`
Calculates the p-th quantile (0 ≤ p ≤ 1) using linear interpolation.
```typescript
**Example:**
```javascript
import { quantile } from 'orlando-transducers';
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
quantile(data, 0.25); // First quartile (Q1): 3.25
quantile(data, 0.5); // Median (Q2): 5.5
quantile(data, 0.75); // Third quartile (Q3): 7.75
quantile(data, 0.95); // 95th percentile: 9.55
quantile(data, 1.5); // undefined (p out of range)
```
**Use cases:**
- Percentile calculations
- Performance SLAs (p95, p99)
- Outlier detection
---
### `mode(array)`
Finds the most frequently occurring value.
```typescript
**Example:**
```javascript
import { mode } from 'orlando-transducers';
const data = [1, 2, 2, 3, 3, 3, 4];
mode(data); // 3 (appears most often)
const tie = [1, 1, 2, 2];
mode(tie); // 1 (returns first mode if tied)
mode([]); // undefined
```
**Use cases:**
- Finding common values
- Survey analysis
- Pattern detection
---
## Collection Utilities
Non-streaming utility operations for sorting, reversing, and generating sequences.
### `sortBy(array, keyFn)`
Sorts elements by the result of a key function.
```typescript
sortBy<T, K>(array: Array<T>, keyFn: (value: T) => K): Array<T>
```
**Example:**
```javascript
import { sortBy } from 'orlando-transducers';
const users = [
{ name: 'Charlie', age: 30 },
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 35 }
];
const byAge = sortBy(users, u => u.age);
// [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }, { name: 'Bob', age: 35 }]
const byName = sortBy(users, u => u.name);
// [{ name: 'Alice', ... }, { name: 'Bob', ... }, { name: 'Charlie', ... }]
```
**Use cases:**
- Sorting objects by property
- Custom ordering
- Normalized sorting
---
### `sortWith(array, compareFn)`
Sorts with a custom comparator function.
```typescript
sortWith<T>(array: Array<T>, compareFn: (a: T, b: T) => number): Array<T>
```
**Example:**
```javascript
import { sortWith } from 'orlando-transducers';
const numbers = [3, 1, 4, 1, 5];
// Ascending
const asc = sortWith(numbers, (a, b) => a - b);
// [1, 1, 3, 4, 5]
// Descending
const desc = sortWith(numbers, (a, b) => b - a);
// [5, 4, 3, 1, 1]
// Complex comparison
const items = [
{ priority: 1, name: 'b' },
{ priority: 2, name: 'a' },
{ priority: 1, name: 'a' }
];
const sorted = sortWith(items, (a, b) => {
if (a.priority !== b.priority) return a.priority - b.priority;
return a.name.localeCompare(b.name);
});
```
**Use cases:**
- Multi-level sorting
- Custom ordering logic
- Complex comparisons
---
### `reverse(array)`
Reverses the order of elements.
```typescript
reverse<T>(array: Array<T>): Array<T>
```
**Example:**
```javascript
import { reverse } from 'orlando-transducers';
const data = [1, 2, 3, 4, 5];
const reversed = reverse(data);
// reversed: [5, 4, 3, 2, 1]
// With pipeline
const pipeline = new Pipeline().filter(x => x % 2 === 0);
const evens = pipeline.toArray([1, 2, 3, 4, 5, 6]);
const reversedEvens = reverse(evens);
// reversedEvens: [6, 4, 2]
```
**Use cases:**
- Reversing order
- Last-to-first processing
- Stack operations
---
### `range(start, end, step)`
Generates a numeric sequence from start to end (exclusive) with a given step.
```typescript
range(start: number, end: number, step: number): Array<number>
```
**Example:**
```javascript
import { range } from 'orlando-transducers';
range(0, 10, 1); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(0, 10, 2); // [0, 2, 4, 6, 8]
range(10, 0, -1); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
range(0, 1, 0.1); // [0, 0.1, 0.2, ..., 0.9]
// Use with pipeline
const pipeline = new Pipeline().filter(x => x % 3 === 0);
const divisibleBy3 = pipeline.toArray(range(0, 30, 1));
// [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
```
**Use cases:**
- Generating sequences
- Loop replacements
- Index generation
---
### `repeat(value, n)`
Repeats a value N times.
```typescript
repeat<T>(value: T, n: number): Array<T>
```
**Example:**
```javascript
import { repeat } from 'orlando-transducers';
repeat(0, 5); // [0, 0, 0, 0, 0]
repeat('x', 3); // ['x', 'x', 'x']
repeat([1, 2], 2); // [[1, 2], [1, 2]]
// Initialize array with default values
const defaultScores = repeat(0, 10);
```
**Use cases:**
- Array initialization
- Padding
- Default values
---
### `cycle(array, n)`
Repeats an entire array N times.
```typescript
cycle<T>(array: Array<T>, n: number): Array<T>
```
**Example:**
```javascript
import { cycle } from 'orlando-transducers';
cycle([1, 2, 3], 2); // [1, 2, 3, 1, 2, 3]
cycle(['a', 'b'], 3); // ['a', 'b', 'a', 'b', 'a', 'b']
// Create repeating pattern
const colors = cycle(['red', 'blue', 'green'], 10);
// ['red', 'blue', 'green', 'red', 'blue', 'green', ...]
```
**Use cases:**
- Repeating patterns
- Round-robin scheduling
- Cyclic data
---
### `unfold(seed, fn, limit)`
Generates a sequence by repeatedly applying a function to a seed value.
```typescript
**Example:**
```javascript
import { unfold } from 'orlando-transducers';
// Fibonacci sequence
const fib = unfold(
[0, 1],
([a, b]) => [b, a + b],
10
);
// [[0, 1], [1, 1], [1, 2], [2, 3], [3, 5], [5, 8], ...]
// Powers of 2
const powersOf2 = unfold(1, x => x * 2, 8);
// [2, 4, 8, 16, 32, 64, 128, 256]
// Stops when function returns undefined
const countdown = unfold(5, x => x > 0 ? x - 1 : undefined, 10);
// [4, 3, 2, 1, 0]
```
**Use cases:**
- Generating sequences
- Recursive patterns
- Mathematical series
---
## Path Operations (JavaScript-Specific)
Safe navigation and transformation of nested objects.
### `path(obj, pathArray)`
Safely accesses nested properties using a path array.
```typescript
path(obj: Object, pathArray: Array<string>): any
```
**Example:**
```javascript
import { path } from 'orlando-transducers';
const user = {
profile: {
contact: {
email: 'user@example.com'
}
}
};
path(user, ['profile', 'contact', 'email']);
// 'user@example.com'
path(user, ['profile', 'contact', 'phone']);
// undefined (safe - doesn't throw)
path(user, ['nonexistent', 'path']);
// undefined
```
**Use cases:**
- Safe property access
- Deep object navigation
- Optional chaining alternative
---
### `pathOr(obj, pathArray, defaultValue)`
Like `path`, but returns a default value if the path doesn't exist.
```typescript
pathOr(obj: Object, pathArray: Array<string>, defaultValue: any): any
```
**Example:**
```javascript
import { pathOr } from 'orlando-transducers';
const config = {
server: {
port: 3000
}
};
pathOr(config, ['server', 'port'], 8080);
// 3000
pathOr(config, ['server', 'host'], 'localhost');
// 'localhost' (default)
pathOr(config, ['database', 'url'], 'mongodb://localhost');
// 'mongodb://localhost' (default)
```
**Use cases:**
- Configuration with defaults
- Fallback values
- Safe data access
---
### `evolve(obj, transformations)`
Applies transformations to nested object properties immutably.
```typescript
evolve(obj: Object, transformations: Object): Object
```
**Example:**
```javascript
import { evolve } from 'orlando-transducers';
const user = {
name: 'john',
age: 30,
contact: {
email: 'JOHN@EXAMPLE.COM'
}
};
const normalized = evolve(user, {
name: (s) => s.toUpperCase(),
age: (n) => n + 1,
contact: {
email: (s) => s.toLowerCase()
}
});
// normalized: {
// name: 'JOHN',
// age: 31,
// contact: { email: 'john@example.com' }
// }
// Original is unchanged (immutable)
console.log(user.name); // 'john'
```
**Use cases:**
- Data normalization
- Immutable updates
- Nested transformations
- API response formatting
---
## Logic Functions
Orlando provides predicate combinators and conditional transducers for cleaner, more declarative conditional logic in pipelines.
### Predicate Combinators
Predicate combinators allow you to build complex predicates from simple building blocks.
#### `both(pred1, pred2)`
Combines two predicates with AND logic.
```typescript
both<T>(pred1: (value: T) => boolean, pred2: (value: T) => boolean): (value: T) => boolean
```
**Example:**
```javascript
import { both } from 'orlando-transducers';
const isPositive = x => x > 0;
const isEven = x => x % 2 === 0;
const isPositiveEven = both(isPositive, isEven);
const pipeline = new Pipeline()
.filter(isPositiveEven);
pipeline.toArray([-2, -1, 0, 1, 2, 3, 4]); // [2, 4]
```
**Use cases:**
- Combining multiple conditions
- Complex validation rules
- Multi-criteria filtering
---
#### `either(pred1, pred2)`
Combines two predicates with OR logic.
```typescript
either<T>(pred1: (value: T) => boolean, pred2: (value: T) => boolean): (value: T) => boolean
```
**Example:**
```javascript
import { either } from 'orlando-transducers';
const isSmall = x => x < 10;
const isLarge = x => x > 100;
const isExtreme = either(isSmall, isLarge);
const pipeline = new Pipeline()
.filter(isExtreme);
pipeline.toArray([5, 50, 150]); // [5, 150]
```
---
#### `complement(predicate)`
Negates a predicate (returns the opposite).
```typescript
complement<T>(pred: (value: T) => boolean): (value: T) => boolean
```
**Example:**
```javascript
import { complement } from 'orlando-transducers';
const isEven = x => x % 2 === 0;
const isOdd = complement(isEven);
const pipeline = new Pipeline()
.filter(isOdd);
pipeline.toArray([1, 2, 3, 4, 5]); // [1, 3, 5]
```
---
#### `allPass(predicates)`
Returns true only if ALL predicates pass (short-circuits on first false).
```typescript
allPass<T>(predicates: Array<(value: T) => boolean>): (value: T) => boolean
```
**Example:**
```javascript
import { allPass } from 'orlando-transducers';
const validUser = allPass([
user => user.age >= 18,
user => user.email.includes('@'),
user => user.verified === true,
user => user.active === true
]);
const pipeline = new Pipeline()
.filter(validUser);
const valid = pipeline.toArray(users);
```
**Use cases:**
- Multi-criteria validation
- Complex rule checking
- Access control
- Data quality checks
---
#### `anyPass(predicates)`
Returns true if ANY predicate passes (short-circuits on first true).
```typescript
anyPass<T>(predicates: Array<(value: T) => boolean>): (value: T) => boolean
```
**Example:**
```javascript
import { anyPass } from 'orlando-transducers';
const isSpecial = anyPass([
x => x === 0,
x => x % 10 === 0,
x => x > 1000
]);
const pipeline = new Pipeline()
.filter(isSpecial);
pipeline.toArray([0, 5, 50, 1500, 7]); // [0, 50, 1500]
```
---
### Conditional Transducers
Conditional transducers apply transformations based on predicates, allowing you to branch logic within a pipeline.
#### `When(predicate, transform)`
Applies transform only when predicate is true. Otherwise, value passes through unchanged.
**Note:** When is a transducer class that needs to be instantiated and used with collectors.
**Example:**
```javascript
import { When, toArray } from 'orlando-transducers';
const doubleIfPositive = new When(
x => x > 0,
x => x * 2
);
const result = toArray(doubleIfPositive, [-1, 2, -3, 4]);
// result: [-1, 4, -3, 8]
```
**Use cases:**
- Conditional normalization
- Selective transformation
- Data correction
- Format conversion
---
#### `Unless(predicate, transform)`
Applies transform only when predicate is false. Inverse of When.
**Example:**
```javascript
import { Unless, toArray } from 'orlando-transducers';
const zeroIfNegative = new Unless(
x => x > 0,
_ => 0
);
const result = toArray(zeroIfNegative, [-1, 2, -3, 4]);
// result: [0, 2, 0, 4]
```
---
#### `IfElse(predicate, onTrue, onFalse)`
Branches on condition - applies different transforms based on predicate.
**Example:**
```javascript
import { IfElse, toArray } from 'orlando-transducers';
const normalize = new IfElse(
x => x >= 0,
x => x * 2, // double if positive
x => x / 2 // halve if negative
);
const result = toArray(normalize, [-4, 3, -6, 5]);
// result: [-2, 6, -3, 10]
```
**Use cases:**
- Two-way data transformation
- A/B processing
- Type-based handling
- Status-dependent logic
---
### Composing Logic Functions
Logic functions compose beautifully for complex conditional logic:
```javascript
import { both, either, complement, allPass, When } from 'orlando-transducers';
// Complex predicate composition
const isPositiveEven = both(x => x > 0, x => x % 2 === 0);
const isNegativeOdd = both(x => x < 0, complement(x => x % 2 === 0));
const isSpecial = either(isPositiveEven, isNegativeOdd);
const pipeline = new Pipeline()
.filter(isSpecial)
.map(x => x * 10);
// Nested logic with When
const complexTransform = new When(
allPass([
x => x > 0,
x => x < 100,
x => x % 2 === 0
]),
x => x * 2
);
```
---
## Functional Lenses (Optics)
Orlando provides functional lenses for clean, composable, immutable updates to nested data structures. Lenses eliminate verbose object spreading and provide a principled way to work with nested data.
### What are Lenses?
A lens is a first-class accessor that focuses on a part of a data structure, enabling immutable get/set/transform operations.
**Traditional approach** (verbose and error-prone):
```javascript
// Deep nested update with spreading
const updateCity = (user, newCity) => ({
...user,
address: {
...user.address,
city: newCity
}
});
```
**Orlando lenses** (clean and composable):
```javascript
import { lensPath } from 'orlando-transducers';
const cityLens = lensPath(['address', 'city']);
const updated = cityLens.set(user, newCity);
```
### Why Lenses?
1. **No nested spreading** - Clean syntax for deep updates
2. **Composable** - Build complex accessors from simple ones
3. **Type-safe** - Optional lenses for nullable fields
4. **Streaming** - Combine with transducers for bounded-memory processing
---
### Creating Lenses
#### `lens(property)`
Creates a lens focusing on a single property.
```typescript
lens(property: string): JsLens
```
**Example:**
```javascript
import { lens } from 'orlando-transducers';
const user = { name: 'Alice', age: 30 };
const nameLens = lens('name');
console.log(nameLens.get(user)); // 'Alice'
const updated = nameLens.set(user, 'Bob');
console.log(updated.name); // 'Bob'
console.log(user.name); // 'Alice' (original unchanged)
```
**Use cases:**
- Simple property access
- Single-level immutable updates
- Building blocks for composition
---
#### `lensPath(path)`
Creates a lens focusing on a deep nested property via an array path.
```typescript
lensPath(path: Array<string>): JsLens
```
**Example:**
```javascript
import { lensPath } from 'orlando-transducers';
const user = {
name: 'Alice',
address: {
city: 'NYC',
zip: '10001'
}
};
const cityLens = lensPath(['address', 'city']);
console.log(cityLens.get(user)); // 'NYC'
const updated = cityLens.set(user, 'LA');
console.log(updated.address.city); // 'LA'
console.log(user.address.city); // 'NYC' (original unchanged)
```
**Use cases:**
- Deep nested updates
- Avoiding manual composition
- Clean multi-level access
---
#### `optional(property)`
Creates an optional lens for properties that may not exist.
```typescript
optional(property: string): JsOptional
```
**Example:**
```javascript
import { optional } from 'orlando-transducers';
const addressLens = optional('address');
const user1 = { name: 'Alice', address: { city: 'NYC' } };
const user2 = { name: 'Bob' };
console.log(addressLens.get(user1)); // { city: 'NYC' }
console.log(addressLens.get(user2)); // undefined
// over is a no-op when value doesn't exist
const updated = addressLens.over(user2, addr => ({ ...addr, city: 'LA' }));
console.log(updated); // { name: 'Bob' } (unchanged)
```
**Use cases:**
- Nullable fields
- Safe optional access
- Conditional updates
- Configuration with optional fields
---
### Lens Methods
All lenses (created with `lens()` or `lensPath()`) support these methods:
#### `get(source)`
Extracts the focused value from the source object.
```typescript
get(source: Object): any
```
**Example:**
```javascript
const nameLens = lens('name');
const user = { name: 'Alice', age: 30 };
console.log(nameLens.get(user)); // 'Alice'
```
---
#### `set(source, value)`
Updates the focused value immutably, returning a new object.
```typescript
set(source: Object, value: any): Object
```
**Example:**
```javascript
const nameLens = lens('name');
const user = { name: 'Alice', age: 30 };
const updated = nameLens.set(user, 'Bob');
// updated: { name: 'Bob', age: 30 }
// user is unchanged
```
**Important:** This returns a **new object**. The original is never modified.
---
#### `over(source, fn)`
Transforms the focused value by applying a function.
```typescript
over(source: Object, fn: (value: any) => any): Object
```
**Example:**
```javascript
const ageLens = lens('age');
const user = { name: 'Alice', age: 30 };
const older = ageLens.over(user, age => age + 1);
// older: { name: 'Alice', age: 31 }
const nameLens = lens('name');
const upper = nameLens.over(user, name => name.toUpperCase());
// upper: { name: 'ALICE', age: 30 }
```
**Use cases:**
- Incrementing counters
- String transformations
- Mathematical operations
- Any computed update
---
#### `compose(otherLens)`
Composes two lenses to focus deeper into nested structures.
```typescript
compose(otherLens: JsLens): JsLens
```
**Example:**
```javascript
const addressLens = lens('address');
const cityLens = lens('city');
const fullLens = addressLens.compose(cityLens);
const user = {
name: 'Alice',
address: { city: 'NYC', zip: '10001' }
};
console.log(fullLens.get(user)); // 'NYC'
const updated = fullLens.set(user, 'LA');
console.log(updated.address.city); // 'LA'
```
**Composition is associative:**
```javascript
// These are equivalent
lensPath(['a', 'b', 'c'])
lens('a').compose(lens('b')).compose(lens('c'))
```
---
### Optional Methods
Optionals (created with `optional()`) support these methods:
#### `get(source)`
Extracts the focused value, returning `undefined` if it doesn't exist.
```typescript
**Example:**
```javascript
const addressLens = optional('address');
const user1 = { name: 'Alice', address: { city: 'NYC' } };
const user2 = { name: 'Bob' };
console.log(addressLens.get(user1)); // { city: 'NYC' }
console.log(addressLens.get(user2)); // undefined
```
---
#### `getOr(source, default)`
Extracts the focused value with a fallback default.
```typescript
getOr(source: Object, default: any): any
```
**Example:**
```javascript
const addressLens = optional('address');
const user = { name: 'Bob' };
const addr = addressLens.getOr(user, { city: 'Unknown', zip: '00000' });
// addr: { city: 'Unknown', zip: '00000' }
```
**Use cases:**
- Configuration with defaults
- Safe data access
- Fallback values
---
#### `set(source, value)`
Updates the focused value immutably (creates the property if it doesn't exist).
```typescript
set(source: Object, value: any): Object
```
**Example:**
```javascript
const phoneLens = optional('phone');
const user = { name: 'Alice' };
const updated = phoneLens.set(user, '555-1234');
// updated: { name: 'Alice', phone: '555-1234' }
```
---
#### `over(source, fn)`
Transforms the focused value only if it exists. Returns source unchanged if value is missing.
```typescript
over(source: Object, fn: (value: any) => any): Object
```
**Example:**
```javascript
const discountLens = optional('discount');
const item1 = { price: 100, discount: 10 };
const item2 = { price: 100 };
const doubled1 = discountLens.over(item1, d => d * 2);
// doubled1: { price: 100, discount: 20 }
const doubled2 = discountLens.over(item2, d => d * 2);
// doubled2: { price: 100 } (unchanged - no discount to transform)
```
---
### React State Management Example
Lenses are perfect for React state updates:
```javascript
import { useState } from 'react';
import { lens, lensPath } from 'orlando-transducers';
function UserProfile() {
const [user, setUser] = useState({
name: 'Alice',
email: 'alice@example.com',
preferences: {
theme: 'dark',
notifications: true
}
});
const nameLens = lens('name');
const themeLens = lensPath(['preferences', 'theme']);
const updateName = (newName) => {
setUser(nameLens.set(user, newName));
};
const toggleTheme = () => {
setUser(themeLens.over(user, theme =>
theme === 'dark' ? 'light' : 'dark'
));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateName(e.target.value)}
/>
<button onClick={toggleTheme}>
Theme: {user.preferences.theme}
</button>
</div>
);
}
```
**Benefits:**
- No nested spreading
- Type-safe with TypeScript
- Reusable lens definitions
- Clear, declarative intent
---
### Shopping Cart Example
Complex state management made simple:
```javascript
import { useState } from 'react';
import { lens, optional } from 'orlando-transducers';
function ShoppingCart() {
const [cart, setCart] = useState({
items: [
{ id: 1, name: 'Widget', quantity: 2, price: 10 },
{ id: 2, name: 'Gadget', quantity: 1, price: 20 }
],
discount: null
});
const itemsLens = lens('items');
const discountLens = optional('discount');
const updateQuantity = (itemId, newQuantity) => {
const updated = cart.items.map(item =>
item.id === itemId
? lens('quantity').set(item, newQuantity)
: item
);
setCart(itemsLens.set(cart, updated));
};
const applyDiscount = (discountCode) => {
setCart(discountLens.set(cart, { code: discountCode, percent: 10 }));
};
const total = cart.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
const discountAmount = discountLens.get(cart)
? total * (cart.discount.percent / 100)
: 0;
return (
<div>
{cart.items.map(item => (
<div key={item.id}>
{item.name} × {item.quantity}
<button onClick={() => updateQuantity(item.id, item.quantity + 1)}>
+
</button>
</div>
))}
<p>Subtotal: ${total}</p>
{discountAmount > 0 && <p>Discount: -${discountAmount}</p>}
<p>Total: ${total - discountAmount}</p>
</div>
);
}
```
---
### Streaming Lenses: The Killer Feature ⚡
**Orlando is the first lens library to integrate with transducers** for streaming data processing with lenses.
#### Traditional Approach (Multiple Passes)
```javascript
// 1. Extract nested values (full array iteration)
const cities = users.map(user => user.address.city);
// 2. Filter (full array iteration)
const nyCities = cities.filter(city => city === 'NYC');
// 3. Unique (full array iteration)
const unique = [...new Set(nyCities)];
// Result: 3 full passes through data
```
#### Orlando Streaming Lenses (Single Pass)
```javascript
import { lens, Pipeline } from 'orlando-transducers';
const cityLens = lensPath(['address', 'city']);
const unique = new Pipeline()
.map(user => cityLens.get(user)) // Extract with lens
.filter(city => city === 'NYC') // Filter
.unique() // Deduplicate
.toArray(users); // Single pass!
```
**Benefits:**
- Single pass through data
- No intermediate arrays
- Bounded memory usage
- Early termination support
- WASM performance
#### Bulk Updates with Lenses + Transducers
```javascript
import { lens, Pipeline } from 'orlando-transducers';
const users = [
{ name: 'alice', email: 'ALICE@EXAMPLE.COM', age: 30 },
{ name: 'bob', email: 'BOB@EXAMPLE.COM', age: 25 }
];
const nameLens = lens('name');
const emailLens = lens('email');
const ageLens = lens('age');
// Normalize all users in a single pass
const normalized = new Pipeline()
.map(user => nameLens.over(user, name => name.toUpperCase()))
.map(user => emailLens.over(user, email => email.toLowerCase()))
.map(user => ageLens.over(user, age => age + 1))
.toArray(users);
// Result: [
// { name: 'ALICE', email: 'alice@example.com', age: 31 },
// { name: 'BOB', email: 'bob@example.com', age: 26 }
// ]
```
---
### Lens Laws
Orlando lenses satisfy three mathematical laws guaranteeing correctness:
#### 1. GetPut Law
```javascript
// Setting what you get doesn't change the object
const l = lens('name');
const obj = { name: 'Alice' };
l.set(obj, l.get(obj)) === obj // ✓ true (structurally)
```
#### 2. PutGet Law
```javascript
// Getting what you just set returns that value
const l = lens('name');
const obj = { name: 'Alice' };
l.get(l.set(obj, 'Bob')) === 'Bob' // ✓ true
```
#### 3. PutPut Law
```javascript
// Second set wins
const l = lens('name');
const obj = { name: 'Alice' };
l.set(l.set(obj, 'Bob'), 'Charlie') === l.set(obj, 'Charlie') // ✓ true (structurally)
```
These laws are automatically verified via property-based tests in Orlando's test suite.
---
### TypeScript Support
Lenses work seamlessly with TypeScript:
```typescript
import { lens, lensPath, optional } from 'orlando-transducers';
interface User {
name: string;
age: number;
address?: {
city: string;
zip: string;
};
}
const user: User = {
name: 'Alice',
age: 30,
address: { city: 'NYC', zip: '10001' }
};
const nameLens = lens('name');
const cityLens = lensPath(['address', 'city']);
const addressLens = optional('address');
const updated: User = nameLens.set(user, 'Bob');
const city: any = cityLens.get(user);
const addr: any = addressLens.getOr(user, { city: 'Unknown', zip: '00000' });
```
---
### Performance Tips
1. **Create lenses once, reuse them:**
```javascript
const nameLens = lens('name');
const updateUser = (user) => nameLens.set(user, 'New Name');
const updateUser = (user) => lens('name').set(user, 'New Name');
```
2. **Use lensPath for deep nesting:**
```javascript
const cityLens = lensPath(['address', 'location', 'city']);
const cityLens = lens('address')
.compose(lens('location'))
.compose(lens('city'));
```
3. **Combine with transducers for large datasets:**
```javascript
const nameLens = lens('name');
const normalized = new Pipeline()
.map(user => nameLens.over(user, name => name.trim()))
.filter(user => user.active)
.toArray(largeDataset);
```
---
## Advanced Optics
Orlando provides a complete optics hierarchy beyond basic lenses. Each optic type is designed for a specific data access pattern.
### Prism
A Prism focuses on a value that may or may not exist within a sum type (tagged union, enum variant). Use prisms when your data has multiple possible shapes and you want to safely access one variant.
#### `prism(matchFn, buildFn)`
Create a prism from match and build functions.
```typescript
**Parameters:**
- `matchFn` - A function that extracts the variant's value, or returns `undefined` if it doesn't match
- `buildFn` - A function that constructs the sum type from a value
**Example:**
```javascript
import { prism } from 'orlando-transducers';
// Prism for Result-like tagged unions
const okPrism = prism(
obj => obj.tag === 'ok' ? obj.value : undefined,
value => ({ tag: 'ok', value })
);
okPrism.preview({ tag: 'ok', value: 42 }); // 42
okPrism.preview({ tag: 'err', error: 'x' }); // undefined
okPrism.review(99); // { tag: 'ok', value: 99 }
// over only applies when the prism matches
okPrism.over({ tag: 'ok', value: 10 }, x => x * 2);
// { tag: 'ok', value: 20 }
okPrism.over({ tag: 'err', error: 'x' }, x => x * 2);
// { tag: 'err', error: 'x' } (unchanged)
```
**Use cases:**
- Accessing specific variants in tagged unions
- Safely transforming optional/nullable structures
- Pattern matching on discriminated types
---
#### Prism Methods
##### `preview(source)`
Try to extract the focused variant from the source.
```typescript
Returns the focused value if the prism matches, or `undefined` if it doesn't.
---
##### `review(value)`
Construct the sum type from the focused value.
```typescript
review(value: any): any
```
---
##### `over(source, fn)`
Transform the focused variant using a function. If the prism doesn't match, returns the source unchanged.
```typescript
over(source: any, fn: (value: any) => any): any
```
---
### Iso
An Iso (isomorphism) represents a lossless bidirectional conversion between two representations. The conversion must be invertible: `from(to(x)) === x` and `to(from(y)) === y`.
#### `iso(toFn, fromFn)`
Create an isomorphism from a pair of inverse functions.
```typescript
iso(toFn: (source: any) => any, fromFn: (target: any) => any): JsIso
```
**Parameters:**
- `toFn` - A function that converts source → target
- `fromFn` - A function that converts target → source (must be the inverse of `toFn`)
**Example:**
```javascript
import { iso } from 'orlando-transducers';
// Celsius ↔ Fahrenheit
const tempIso = iso(
c => c * 9 / 5 + 32, // to: Celsius → Fahrenheit
f => (f - 32) * 5 / 9 // from: Fahrenheit → Celsius
);
tempIso.to(100); // 212
tempIso.from(32); // 0
// over: convert, transform in target space, convert back
tempIso.over(0, f => f + 10); // ~5.56 (add 10°F, result in Celsius)
// Reverse the iso
const fahrenheitCelsius = tempIso.reverse();
fahrenheitCelsius.to(212); // 100
```
**Use cases:**
- Unit conversions (temperature, currency, distance)
- Encoding/decoding (JSON ↔ object, base64 ↔ binary)
- Newtype wrappers
---
#### Iso Methods
##### `to(source)`
Convert from source representation to target.
```typescript
to(source: any): any
```
---
##### `from(value)`
Convert from target representation back to source.
```typescript
from(value: any): any
```
---
##### `over(source, fn)`
Transform via the isomorphism: convert to target, apply `fn`, convert back.
```typescript
over(source: any, fn: (target: any) => any): any
```
---
##### `reverse()`
Return a new Iso with the `to` and `from` directions swapped.
```typescript
reverse(): JsIso
```
---
### Fold
A Fold is a read-only optic that extracts zero or more values from a structure. Unlike a Lens (which focuses on exactly one value) or a Traversal (which can also write), a Fold is purely for reading.
#### `fold(extractFn)`
Create a fold from an extraction function.
```typescript
fold(extractFn: (source: any) => any[]): JsFold
```
**Parameters:**
- `extractFn` - A function that returns an array of extracted values from the source
**Example:**
```javascript
import { fold } from 'orlando-transducers';
// Extract all values from an object
const valuesFold = fold(obj => Object.values(obj));
valuesFold.foldOf({ a: 1, b: 2, c: 3 }); // [1, 2, 3]
valuesFold.length({ a: 1, b: 2, c: 3 }); // 3
valuesFold.first({ a: 1, b: 2, c: 3 }); // 1
valuesFold.isEmpty({}); // true
// Fold over nested arrays
const flatFold = fold(arr => arr.flat());
flatFold.foldOf([[1, 2], [3, 4]]); // [1, 2, 3, 4]
```
**Use cases:**
- Aggregating values from complex structures
- Counting elements matching a pattern
- Extracting the first match from a collection
---
#### Fold Methods
##### `foldOf(source)`
Extract all focused values from the source.
```typescript
foldOf(source: any): any[]
```
---
##### `isEmpty(source)`
Check whether the fold extracts zero values.
```typescript
isEmpty(source: any): boolean
```
---
##### `length(source)`
Count how many values the fold extracts.
```typescript
length(source: any): number
```
---
##### `first(source)`
Extract the first focused value, or `undefined` if empty.
```typescript
---
### Traversal
A Traversal focuses on multiple values within a structure, supporting both read and write. It generalizes a Lens to work over collections.
#### `traversal(getAllFn, overAllFn)`
Create a traversal from get-all and over-all functions.
```typescript
traversal(
getAllFn: (source: any) => any[],
overAllFn: (source: any, fn: (value: any) => any) => any
): JsTraversal
```
**Parameters:**
- `getAllFn` - A function that extracts all focused values from the source
- `overAllFn` - A function that applies a transformation to all focused values and returns a new structure
**Example:**
```javascript
import { traversal } from 'orlando-transducers';
// Traversal over array elements
const elemTraversal = traversal(
arr => arr,
(arr, fn) => arr.map(v => fn(v))
);
const nums = [1, 2, 3, 4];
elemTraversal.getAll(nums); // [1, 2, 3, 4]
elemTraversal.setAll(nums, 0); // [0, 0, 0, 0]
elemTraversal.overAll(nums, x => x * 10); // [10, 20, 30, 40]
// Traversal over object values
const valuesTraversal = traversal(
obj => Object.values(obj),
(obj, fn) => Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, fn(v)])
)
);
valuesTraversal.getAll({ x: 1, y: 2 }); // [1, 2]
valuesTraversal.overAll({ x: 1, y: 2 }, n => n + 10); // { x: 11, y: 12 }
```
**Use cases:**
- Transforming all elements in a collection
- Updating nested arrays within objects
- Batch modifications to homogeneous structures
---
#### Traversal Methods
##### `getAll(source)`
Extract all focused values.
```typescript
getAll(source: any): any[]
```
---
##### `setAll(source, value)`
Replace all focused values with a single value.
```typescript
setAll(source: any, value: any): any
```
---
##### `overAll(source, fn)`
Transform all focused values using a function.
```typescript
overAll(source: any, fn: (value: any) => any): any
```
---
## Pipeline Enhancements
JavaScript-specific convenience methods for common data transformation patterns.
### `pluck(key)`
Extract a single property from each object in the stream. Equivalent to `.map(x => x[key])`.
```typescript
pluck(key: string): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline().pluck('name');
pipeline.toArray([
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 }
]);
// ['Alice', 'Bob']
```
**Use cases:**
- Extracting IDs from objects
- Pulling out a single field for aggregation
- Quick property access in data pipelines
---
### `project(keys)`
Extract multiple properties from each object, creating new objects with only those keys.
```typescript
project(keys: string[]): Pipeline
```
**Example:**
```javascript
const pipeline = new Pipeline().project(['id', 'name']);
pipeline.toArray([
{ id: 1, name: 'Alice', age: 30, email: 'alice@test.com' },
{ id: 2, name: 'Bob', age: 25, email: 'bob@test.com' }
]);
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
```
**Use cases:**
- Selecting fields for API responses
- Reducing object size before serialization
- Creating view models from full data objects
---
### `compact()`
Remove all falsy values from the stream.
```typescript
compact(): Pipeline
```
Filters out: `null`, `undefined`, `false`, `0`, `NaN`, and `''` (empty string).
**Example:**
```javascript
const pipeline = new Pipeline().compact();
pipeline.toArray([1, null, 'hello', undefined, 0, false, '', 42]);
// [1, 'hello', 42]
```
**Use cases:**
- Cleaning up sparse data
- Removing empty/null entries from API responses
- Filtering out zero values from numeric data
---
### `flatten(depth)`
Flatten nested arrays to a given depth.
```typescript
flatten(depth: number): Pipeline
```
**Parameters:**
- `depth` - Maximum depth to flatten. Use `1` for a single level.
**Example:**
```javascript
const pipeline = new Pipeline().flatten(1);
pipeline.toArray([[1, 2], [3, [4, 5]], [6]]);
// [1, 2, 3, [4, 5], 6]
const deep = new Pipeline().flatten(2);
deep.toArray([[1, 2], [3, [4, 5]], [6]]);
// [1, 2, 3, 4, 5, 6]
```
**Use cases:**
- Normalizing nested data structures
- Flattening grouped results
- Processing tree-like data
---
### `whereMatches(spec)`
Filter objects that match all properties in a spec object. Uses strict equality for comparison.
```typescript
whereMatches(spec: object): Pipeline
```
**Parameters:**
- `spec` - A JavaScript object whose key-value pairs must all match
**Example:**
```javascript
const pipeline = new Pipeline().whereMatches({ active: true, role: 'admin' });
pipeline.toArray([
{ name: 'Alice', active: true, role: 'admin' },
{ name: 'Bob', active: false, role: 'user' },
{ name: 'Charlie', active: true, role: 'user' }
]);
// [{ name: 'Alice', active: true, role: 'admin' }]
```
**Use cases:**
- Filtering records by multiple criteria
- Pattern matching on object shapes
- Declarative query-like filtering
---
## Optics-Pipeline Integration
Use lenses directly within pipelines for focused, composable data transformations. These methods avoid the overhead of creating intermediate JavaScript functions.
### `viewLens(lens)`
Apply a lens to extract the focused value from each element. Equivalent to `.map(x => myLens.get(x))`.
```typescript
viewLens(lens: JsLens): Pipeline
```
**Example:**
```javascript
import { lens, Pipeline } from 'orlando-transducers';
const nameLens = lens('name');
const pipeline = new Pipeline().viewLens(nameLens);
pipeline.toArray([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);
// ['Alice', 'Bob']
// Compose with other pipeline operations
const pipeline2 = new Pipeline()
.viewLens(nameLens)
.filter(name => name.length > 3)
.take(2);
```
---
### `overLens(lens, fn)`
Transform each element's focused value through a lens. The rest of each object is preserved unchanged.
```typescript
overLens(lens: JsLens, fn: (value: any) => any): Pipeline
```
**Example:**
```javascript
import { lens, Pipeline } from 'orlando-transducers';
const priceLens = lens('price');
const pipeline = new Pipeline().overLens(priceLens, p => p * 0.9);
pipeline.toArray([
{ name: 'Widget', price: 100 },
{ name: 'Gadget', price: 200 }
]);
// [{ name: 'Widget', price: 90 }, { name: 'Gadget', price: 180 }]
```
---
### `filterLens(lens, pred)`
Filter elements based on a predicate applied to the lens-focused value. Equivalent to `.filter(x => pred(myLens.get(x)))`.
```typescript
filterLens(lens: JsLens, pred: (value: any) => boolean): Pipeline
```
**Example:**
```javascript
import { lens, Pipeline } from 'orlando-transducers';
const ageLens = lens('age');
const pipeline = new Pipeline().filterLens(ageLens, a => a >= 18);
pipeline.toArray([
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 }
]);
// [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }]
```
---
### `setLens(lens, value)`
Set the focused value of a lens on every element. Useful for bulk updates.
```typescript
setLens(lens: JsLens, value: any): Pipeline
```
**Example:**
```javascript
import { lens, Pipeline } from 'orlando-transducers';
const statusLens = lens('status');
const pipeline = new Pipeline().setLens(statusLens, 'published');
pipeline.toArray([
{ id: 1, status: 'draft' },
{ id: 2, status: 'draft' }
]);
// [{ id: 1, status: 'published' }, { id: 2, status: 'published' }]
```
---
## Geometric Optics
Operations on multivector coefficient arrays for Clifford algebras. These functions work with flat `Float64Array` coefficient arrays where each index corresponds to a basis blade (index = bitset of basis vectors, grade = popcount of bits).
A multivector in Cl(p,q,r) has `2^(p+q+r)` coefficients. For example, Cl(3,0,0) has 8 basis blades: scalar, e1, e2, e3, e12, e13, e23, e123.
### `bladeGrade(index)`
Compute the grade (number of basis vectors) of a basis blade from its bit-index.
```typescript
bladeGrade(index: number): number
```
**Example:**
```javascript
import { bladeGrade } from 'orlando-transducers';
bladeGrade(0); // 0 (scalar: no basis vectors)
bladeGrade(1); // 1 (e1: one basis vector)
bladeGrade(3); // 2 (e12: two basis vectors, binary 0b11)
bladeGrade(7); // 3 (e123: three basis vectors, binary 0b111)
bladeGrade(5); // 2 (e13: two basis vectors, binary 0b101)
```
---
### `bladesAtGradeCount(dimension, grade)`
Count how many basis blades exist at a given grade in a given dimension.
```typescript
bladesAtGradeCount(dimension: number, grade: number): number
```
**Example:**
```javascript
import { bladesAtGradeCount } from 'orlando-transducers';
// In 3 dimensions:
bladesAtGradeCount(3, 0); // 1 (scalar)
bladesAtGradeCount(3, 1); // 3 (vectors: e1, e2, e3)
bladesAtGradeCount(3, 2); // 3 (bivectors: e12, e13, e23)
bladesAtGradeCount(3, 3); // 1 (trivector: e123)
```
---
### `gradeIndices(dimension, grade)`
Get the coefficient array indices for all basis blades at a given grade.
```typescript
gradeIndices(dimension: number, grade: number): number[]
```
**Example:**
```javascript
import { gradeIndices } from 'orlando-transducers';
gradeIndices(3, 0); // [0] (scalar at index 0)
gradeIndices(3, 1); // [1, 2, 4] (e1, e2, e3)
gradeIndices(3, 2); // [3, 5, 6] (e12, e13, e23)
gradeIndices(3, 3); // [7] (e123)
```
---
### `gradeExtract(dimension, grade, mv)`
Extract the coefficients of a specific grade from a multivector.
```typescript
gradeExtract(dimension: number, grade: number, mv: Float64Array): Float64Array
```
**Example:**
```javascript
import { gradeExtract } from 'orlando-transducers';
// Cl(3,0,0): [scalar, e1, e2, e3, e12, e13, e23, e123]
const mv = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
gradeExtract(3, 0, mv); // Float64Array [1] (scalar)
gradeExtract(3, 1, mv); // Float64Array [2, 3, 4] (vectors)
gradeExtract(3, 2, mv); // Float64Array [5, 6, 7] (bivectors)
gradeExtract(3, 3, mv); // Float64Array [8] (trivector)
```
---
### `gradeProject(dimension, grade, mv)`
Project a multivector onto a single grade, zeroing out all other components.
```typescript
gradeProject(dimension: number, grade: number, mv: Float64Array): Float64Array
```
**Example:**
```javascript
import { gradeProject } from 'orlando-transducers';
const mv = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
// Keep only the vector (grade 1) part
gradeProject(3, 1, mv);
// Float64Array [0, 2, 3, 0, 0, 0, 0, 0]
// ^scalar zeroed ^bivectors zeroed
```
---
### `gradeProjectMax(dimension, maxGrade, mv)`
Project a multivector onto all grades up to and including `maxGrade`.
```typescript
gradeProjectMax(dimension: number, maxGrade: number, mv: Float64Array): Float64Array
```
**Example:**
```javascript
import { gradeProjectMax } from 'orlando-transducers';
const mv = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
// Keep scalar + vector parts (grades 0 and 1)
gradeProjectMax(3, 1, mv);
// Float64Array [1, 2, 3, 4, 0, 0, 0, 0]
```
---
### `gradeMask(dimension, mv)`
Return a bitmask indicating which grades have non-zero coefficients.
```typescript
gradeMask(dimension: number, mv: Float64Array): number
```
**Example:**
```javascript
import { gradeMask } from 'orlando-transducers';
const pureVector = new Float64Array([0, 1, 2, 3, 0, 0, 0, 0]);
gradeMask(3, pureVector); // 0b0010 = 2 (only grade 1)
const mixed = new Float64Array([1, 0, 0, 0, 5, 6, 7, 0]);
gradeMask(3, mixed); // 0b0101 = 5 (grades 0 and 2)
```
---
### `hasGrade(dimension, grade, mv)`
Check if a multivector has any non-zero coefficients at a given grade.
```typescript
hasGrade(dimension: number, grade: number, mv: Float64Array): boolean
```
---
### `isPureGrade(dimension, mv)`
Check if a multivector has non-zero coefficients at exactly one grade.
```typescript
isPureGrade(dimension: number, mv: Float64Array): boolean
```
**Example:**
```javascript
import { isPureGrade } from 'orlando-transducers';
const pureVector = new Float64Array([0, 1, 2, 3, 0, 0, 0, 0]);
isPureGrade(3, pureVector); // true (only grade 1)
const mixed = new Float64Array([1, 1, 0, 0, 0, 0, 0, 0]);
isPureGrade(3, mixed); // false (grades 0 and 1)
```
---
### `componentGet(mv, bladeIndex)`
Get the coefficient at a specific blade index.
```typescript
---
### `componentSet(mv, bladeIndex, value)`
Return a new multivector with one coefficient changed.
```typescript
componentSet(mv: Float64Array, bladeIndex: number, value: number): Float64Array
```
---
### `mvNorm(mv)`
Compute the magnitude (Euclidean norm) of a multivector.
```typescript
mvNorm(mv: Float64Array): number
```
**Example:**
```javascript
import { mvNorm } from 'orlando-transducers';
const mv = new Float64Array([3, 4, 0, 0]);
mvNorm(mv); // 5.0
```
---
### `mvNormSquared(mv)`
Compute the squared magnitude (avoids the square root).
```typescript
mvNormSquared(mv: Float64Array): number
```
---
### `mvNormalize(mv)`
Normalize a multivector to unit length. Returns `undefined` if the norm is zero.
```typescript
**Example:**
```javascript
import { mvNormalize } from 'orlando-transducers';
const mv = new Float64Array([3, 4, 0, 0]);
mvNormalize(mv); // Float64Array [0.6, 0.8, 0, 0]
```
---
### `gradeInvolution(dimension, mv)`
Apply grade involution: grade `k` coefficients are multiplied by `(-1)^k`.
```typescript
gradeInvolution(dimension: number, mv: Float64Array): Float64Array
```
**Example:**
```javascript
import { gradeInvolution } from 'orlando-transducers';
// [scalar, e1, e2, e3, e12, e13, e23, e123]
const mv = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
gradeInvolution(3, mv);
// [1, -2, -3, -4, 5, 6, 7, -8]
// grade 0: +1, grade 1: -1, grade 2: +1, grade 3: -1
```
---
### `mvReverse(dimension, mv)`
Apply the reverse operation: grade `k` coefficients are multiplied by `(-1)^(k(k-1)/2)`.
```typescript
mvReverse(dimension: number, mv: Float64Array): Float64Array
```
**Example:**
```javascript
import { mvReverse } from 'orlando-transducers';
const mv = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]);
mvReverse(3, mv);
// [1, 2, 3, 4, -5, -6, -7, -8]
// grade 0: +1, grade 1: +1, grade 2: -1, grade 3: -1
```
---
## Next Steps
- Check out the [Hybrid Composition Guide](../HYBRID_COMPOSITION.md) for combining transducers with multi-input operations
- See the [Migration Guide](./MIGRATION.md) for converting from array methods
- Read the [Main README](../../README.md) for project overview
---
**Questions?** [Open an issue](https://github.com/justinelliottcobb/Orlando/issues) on GitHub.