List
The List module provides curried, pipe-friendly functions for working with arrays. Every function returns a new array — the original is never mutated.
import * as L from "@oofp/core/list";Transforming
Section titled “Transforming”Transforms each element of an array using a function.
import { pipe } from "@oofp/core/pipe";import * as L from "@oofp/core/list";
pipe([1, 2, 3], L.map((n) => n * 2));// [2, 4, 6]
pipe(["hello", "world"], L.map((s) => s.toUpperCase()));// ["HELLO", "WORLD"]mapIndexed
Section titled “mapIndexed”Transforms each element with access to its index. The mapping function is curried: it receives the index first, then the element.
pipe( ["a", "b", "c"], L.mapIndexed((index) => (item) => `${index}: ${item}`),);// ["0: a", "1: b", "2: c"]Executes a side effect for each element without modifying the array. Returns the original array.
pipe( [1, 2, 3], L.tap((n) => console.log(n)), L.map((n) => n * 2),);// Logs: 1, 2, 3// Result: [2, 4, 6]Filtering
Section titled “Filtering”filter
Section titled “filter”Keeps only elements that satisfy a predicate.
pipe([1, 2, 3, 4, 5], L.filter((n) => n % 2 === 0));// [2, 4]
pipe( ["hello", "", "world", ""], L.filter((s) => s.length > 0),);// ["hello", "world"]distinctBy
Section titled “distinctBy”Removes duplicate elements. Without a key function, uses strict equality. With a key function, deduplicates by the computed key.
pipe([1, 2, 2, 3, 3, 3], L.distinctBy());// [1, 2, 3]
const users = [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 1, name: "Alice (duplicate)" },];
pipe(users, L.distinctBy((u) => String(u.id)));// [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]Reducing
Section titled “Reducing”reduce
Section titled “reduce”Reduces an array to a single value by applying a function from left to right.
pipe( [1, 2, 3, 4], L.reduce(0, (acc, curr) => acc + curr),);// 10
pipe( ["a", "b", "c"], L.reduce("", (acc, curr) => acc + curr),);// "abc"reduceRight
Section titled “reduceRight”Like reduce, but processes elements from right to left.
pipe( ["a", "b", "c"], L.reduceRight("", (acc, curr) => acc + curr),);// "cba"Slicing & Chunking
Section titled “Slicing & Chunking”Returns the first n elements. Safe if n exceeds the array length.
pipe([1, 2, 3, 4, 5], L.take(3));// [1, 2, 3]
pipe([1, 2], L.take(10));// [1, 2]
pipe([1, 2, 3], L.take(0));// []Splits an array into chunks of a given size.
pipe([1, 2, 3, 4, 5], L.chunk(2));// [[1, 2], [3, 4], [5]]
pipe([1, 2, 3, 4, 5, 6], L.chunk(3));// [[1, 2, 3], [4, 5, 6]]Flattening
Section titled “Flattening”flatten
Section titled “flatten”Flattens one level of nesting.
pipe([[1, 2], [3, 4], [5]], L.flatten);// [1, 2, 3, 4, 5]
pipe([["a", "b"], ["c"]], L.flatten);// ["a", "b", "c"]Grouping & Indexing
Section titled “Grouping & Indexing”groupBy
Section titled “groupBy”Groups elements into a record of arrays, keyed by the result of a function.
const users = [ { name: "Alice", role: "admin" }, { name: "Bob", role: "user" }, { name: "Charlie", role: "admin" },];
pipe(users, L.groupBy((u) => u.role));// {// admin: [{ name: "Alice", role: "admin" }, { name: "Charlie", role: "admin" }],// user: [{ name: "Bob", role: "user" }]// }indexBy
Section titled “indexBy”Creates a record keyed by the result of a function, where each key maps to a single element. If multiple elements produce the same key, the last one wins.
const users = [ { id: "1", name: "Alice" }, { id: "2", name: "Bob" },];
pipe(users, L.indexBy((u) => u.id));// { "1": { id: "1", name: "Alice" }, "2": { id: "2", name: "Bob" } }Sorting
Section titled “Sorting”Sorts an array using a comparator function that receives { a, b } and returns a number (negative, zero, or positive).
pipe([3, 1, 4, 1, 5], L.sort(({ a, b }) => a - b));// [1, 1, 3, 4, 5]
pipe([3, 1, 4, 1, 5], L.sort(({ a, b }) => b - a));// [5, 4, 3, 1, 1]
const users = [ { name: "Charlie", age: 25 }, { name: "Alice", age: 30 }, { name: "Bob", age: 20 },];
pipe(users, L.sort(({ a, b }) => a.age - b.age));// [{ name: "Bob", age: 20 }, { name: "Charlie", age: 25 }, { name: "Alice", age: 30 }]The original array is not mutated — sort always returns a new array.
Combining
Section titled “Combining”concat
Section titled “concat”Concatenates two arrays. The argument array comes first, the piped array second.
pipe([3, 4, 5], L.concat([1, 2]));// [1, 2, 3, 4, 5]append
Section titled “append”Adds an element to the end of an array.
pipe([1, 2, 3], L.append(4));// [1, 2, 3, 4]prepend
Section titled “prepend”Adds an element to the beginning of an array.
pipe([2, 3, 4], L.prepend(1));// [1, 2, 3, 4]Searching
Section titled “Searching”Returns the first element that satisfies a predicate, or undefined if none is found.
pipe([1, 2, 3, 4], L.find((n) => n > 2));// 3
pipe([1, 2, 3], L.find((n) => n > 10));// undefinedYou can wrap the result in a Maybe for safer handling:
import { pipe } from "@oofp/core/pipe";import * as L from "@oofp/core/list";import * as M from "@oofp/core/maybe";
const result = pipe( [1, 2, 3, 4], L.find((n) => n > 2), M.fromNullable, M.map((n) => n * 10), M.getOrElse(0),);// 30findMap
Section titled “findMap”Applies a function to each element and returns the first non-undefined result.
const parsePositive = (n: number) => (n > 0 ? n : undefined);
pipe([-1, -2, 3, 4], L.findMap(parsePositive));// 3
pipe([-1, -2], L.findMap(parsePositive));// undefinedJoining
Section titled “Joining”Joins array elements into a string with a separator.
pipe(["hello", "world"], L.join(" "));// "hello world"
pipe([1, 2, 3], L.join(", "));// "1, 2, 3"Updating
Section titled “Updating”update
Section titled “update”Replaces the element at a given index. Returns the original array unchanged if the index is out of bounds.
pipe([1, 2, 3], L.update(1)(20));// [1, 20, 3]
pipe([1, 2, 3], L.update(5)(20));// [1, 2, 3] — index out of bounds, no changeComparing
Section titled “Comparing”equals
Section titled “equals”Shallow comparison of two arrays using strict equality (===).
pipe([1, 2, 3], L.equals([1, 2, 3]));// true
pipe([1, 2, 3], L.equals([1, 2, 4]));// false
pipe([1, 2], L.equals([1, 2, 3]));// falseInspecting
Section titled “Inspecting”isEmpty
Section titled “isEmpty”Returns true if the array has no elements.
L.isEmpty([]); // trueL.isEmpty([1]); // falseReturns the number of elements in the array.
L.size([]); // 0L.size([1, 2, 3]); // 3Practical Examples
Section titled “Practical Examples”Data processing pipeline
Section titled “Data processing pipeline”import { pipe } from "@oofp/core/pipe";import * as L from "@oofp/core/list";
interface Product { name: string; category: string; price: number; inStock: boolean;}
const products: Product[] = [ { name: "Laptop", category: "electronics", price: 999, inStock: true }, { name: "Phone", category: "electronics", price: 699, inStock: true }, { name: "Shirt", category: "clothing", price: 29, inStock: false }, { name: "Shoes", category: "clothing", price: 89, inStock: true }, { name: "Tablet", category: "electronics", price: 499, inStock: true },];
const expensiveInStock = pipe( products, L.filter((p) => p.inStock), L.filter((p) => p.price > 100), L.sort(({ a, b }) => b.price - a.price), L.map((p) => `${p.name}: $${p.price}`), L.join("\n"),);// "Laptop: $999\nPhone: $699\nTablet: $499"Paginating results
Section titled “Paginating results”import { pipe } from "@oofp/core/pipe";import * as L from "@oofp/core/list";
const paginate = <A>(page: number, pageSize: number) => (items: A[]) => pipe( items, L.chunk(pageSize), L.find((_, i) => i === page) ?? [], );
// Or more directly:const getPage = (page: number, pageSize: number) => <A>(items: A[]): A[] => pipe( items, L.chunk(pageSize), (chunks) => chunks[page] ?? [], );Building a lookup table
Section titled “Building a lookup table”import { pipe } from "@oofp/core/pipe";import * as L from "@oofp/core/list";
interface User { id: string; name: string; department: string;}
const users: User[] = [ { id: "1", name: "Alice", department: "Engineering" }, { id: "2", name: "Bob", department: "Marketing" }, { id: "3", name: "Charlie", department: "Engineering" },];
// Quick lookup by IDconst usersById = pipe(users, L.indexBy((u) => u.id));// { "1": { id: "1", ... }, "2": { id: "2", ... }, "3": { id: "3", ... } }
// Group by departmentconst byDepartment = pipe(users, L.groupBy((u) => u.department));// { Engineering: [...], Marketing: [...] }Combining with Maybe
Section titled “Combining with Maybe”import { pipe } from "@oofp/core/pipe";import * as L from "@oofp/core/list";import * as M from "@oofp/core/maybe";
const users = [ { id: 1, name: "Alice", email: "alice@example.com" }, { id: 2, name: "Bob", email: "bob@example.com" },];
const findUserEmail = (id: number) => pipe( users, L.find((u) => u.id === id), M.fromNullable, M.map((u) => u.email), M.getOrElse("unknown@example.com"), );
findUserEmail(1); // "alice@example.com"findUserEmail(9); // "unknown@example.com"