Skip to content

Object

The Object module provides curried, pipe-friendly functions for working with plain objects. Every function produces a new object — the input is never mutated.

import * as O from "@oofp/core/object";

Transforms every value in an object, preserving the keys.

import { pipe } from "@oofp/core/pipe";
import * as O from "@oofp/core/object";
pipe(
{ a: 1, b: 2, c: 3 },
O.mapValues((n) => n * 2),
);
// { a: 2, b: 4, c: 6 }
pipe(
{ name: "alice", role: "admin" },
O.mapValues((s) => s.toUpperCase()),
);
// { name: "ALICE", role: "ADMIN" }

Transforms every value with access to its key. The mapping function is curried: key first, then value.

pipe(
{ host: "localhost", port: "3000" },
O.mapKeyValues((key) => (value) => `${key}=${value}`),
);
// { host: "host=localhost", port: "port=3000" }

Transforms a single property of an object, leaving the rest untouched. The result type is updated automatically.

pipe(
{ name: "alice", age: 25 },
O.mapProperty("name", (s) => s.toUpperCase()),
);
// { name: "ALICE", age: 25 }
pipe(
{ price: 100, currency: "USD" },
O.mapProperty("price", (p) => p * 1.1),
);
// { price: 110, currency: "USD" }

Like mapProperty, but the mapping function receives both the property value and the full object as context.

pipe(
{ price: 100, taxRate: 0.21 },
O.mapPropertywc("price", ({ value, ctx }) => value * (1 + ctx.taxRate)),
);
// { price: 121, taxRate: 0.21 }

Transforms the keys of an object, preserving the values.

pipe(
{ name: "Alice", age: 30 },
O.mapKeys((key) => `user_${key}`),
);
// { user_name: "Alice", user_age: 30 }

Returns an array of all values.

pipe({ a: 1, b: 2, c: 3 }, O.values);
// [1, 2, 3]

Returns an array of all keys, properly typed.

pipe({ name: "Alice", age: 30 }, O.keys);
// ["name", "age"]

Returns an array of [key, value] tuples.

pipe({ a: 1, b: 2 }, O.entries);
// [["a", 1], ["b", 2]]

Constructs an object from an array of [key, value] tuples.

O.fromEntries([["a", 1], ["b", 2]]);
// { a: 1, b: 2 }

Keeps only properties whose values (and keys) satisfy a predicate. Returns a Partial of the original type.

pipe(
{ a: 1, b: 2, c: 3, d: 4 },
O.filter((value) => value > 2),
);
// { c: 3, d: 4 }
pipe(
{ name: "Alice", role: "admin", temp: "" },
O.filter((value) => value.length > 0),
);
// { name: "Alice", role: "admin" }

The predicate also receives the key as its second argument:

pipe(
{ userName: "alice", userAge: 30, id: 1 },
O.filter((_, key) => key.startsWith("user")),
);
// { userName: "alice", userAge: 30 }

Selects only the specified keys.

pipe(
{ name: "Alice", age: 30, email: "alice@test.com" },
O.pick(["name", "email"]),
);
// { name: "Alice", email: "alice@test.com" }

Removes the specified keys.

pipe(
{ name: "Alice", age: 30, password: "secret" },
O.omit(["password"]),
);
// { name: "Alice", age: 30 }

Returns the number of keys in an object.

O.size({ a: 1, b: 2, c: 3 }); // 3
O.size({}); // 0

Returns true if the object has no keys.

O.isEmpty({}); // true
O.isEmpty({ a: 1 }); // false

Checks if a key exists in an object.

pipe({ name: "Alice", age: 30 }, O.has("name")); // true
pipe({ name: "Alice", age: 30 }, O.has("email")); // false

Retrieves a value by key. The key must exist in the type.

pipe({ name: "Alice", age: 30 }, O.get("name"));
// "Alice"

Retrieves a value by key, returning a default if the value is null or undefined.

pipe({ name: "Alice", age: undefined }, O.getOr("age", 0));
// 0
pipe({ name: "Alice", age: 30 }, O.getOr("age", 0));
// 30

Shallow-merges two objects. Properties from the argument override those in the piped object.

pipe(
{ a: 1, b: 2 },
O.merge({ b: 20, c: 30 }),
);
// { a: 1, b: 20, c: 30 }

Recursively merges nested objects. Arrays and non-object values are replaced, not merged.

pipe(
{ db: { host: "localhost", port: 5432 }, debug: false },
O.deepMerge({ db: { port: 3306 }, debug: true }),
);
// { db: { host: "localhost", port: 3306 }, debug: true }

Reduces an object to a single value by iterating over its entries.

pipe(
{ a: 1, b: 2, c: 3 },
O.reduce((acc, value) => acc + value, 0),
);
// 6
pipe(
{ width: 10, height: 20 },
O.reduce((acc, value, key) => [...acc, `${key}: ${value}`], [] as string[]),
);
// ["width: 10", "height: 20"]

Swaps keys and values. Both keys and values must be strings.

pipe(
{ a: "x", b: "y", c: "z" },
O.invert,
);
// { x: "a", y: "b", z: "c" }

Groups an object’s values by the result of a function, producing a record of arrays.

pipe(
{ alice: 30, bob: 25, charlie: 30, diana: 25 },
O.groupBy((age) => (age >= 30 ? "senior" : "junior")),
);
// { senior: [30, 30], junior: [25, 25] }

Returns true if all values satisfy a predicate.

pipe(
{ a: 2, b: 4, c: 6 },
O.every((v) => v % 2 === 0),
);
// true
pipe(
{ a: 2, b: 3, c: 6 },
O.every((v) => v % 2 === 0),
);
// false

Returns true if at least one value satisfies a predicate.

pipe(
{ a: 1, b: 2, c: 3 },
O.some((v) => v > 2),
);
// true
pipe(
{ a: 1, b: 2, c: 3 },
O.some((v) => v > 10),
);
// false

Returns the first [key, value] tuple that satisfies a predicate, or undefined.

pipe(
{ alice: 30, bob: 25, charlie: 35 },
O.find((age) => age > 30),
);
// ["charlie", 35]
pipe(
{ alice: 30, bob: 25 },
O.find((age) => age > 50),
);
// undefined

Builds an object from an array using key and value extractor functions.

const users = [
{ id: "1", name: "Alice" },
{ id: "2", name: "Bob" },
];
pipe(
users,
O.fromArray(
(u) => u.id,
(u) => u.name,
),
);
// { "1": "Alice", "2": "Bob" }

import { pipe } from "@oofp/core/pipe";
import * as O from "@oofp/core/object";
const defaults = {
db: { host: "localhost", port: 5432, pool: 10 },
cache: { ttl: 3600, maxSize: 100 },
debug: false,
};
const overrides = {
db: { port: 3306 },
debug: true,
};
const config = pipe(defaults, O.deepMerge(overrides));
// { db: { host: "localhost", port: 3306, pool: 10 }, cache: { ttl: 3600, maxSize: 100 }, debug: true }
import { pipe } from "@oofp/core/pipe";
import * as O from "@oofp/core/object";
const rawResponse = {
id: "123",
name: "Alice",
password: "hashed",
__internal: "metadata",
email: "alice@example.com",
};
const sanitized = pipe(
rawResponse,
O.omit(["password", "__internal"]),
);
// { id: "123", name: "Alice", email: "alice@example.com" }
import { pipe } from "@oofp/core/pipe";
import * as O from "@oofp/core/object";
interface Product {
name: string;
price: number;
}
const products: Product[] = [
{ name: "Laptop", price: 999 },
{ name: "Phone", price: 699 },
{ name: "Tablet", price: 499 },
];
const priceTable = pipe(
products,
O.fromArray(
(p) => p.name,
(p) => p.price,
),
);
// { Laptop: 999, Phone: 699, Tablet: 499 }
const discounted = pipe(
priceTable,
O.mapValues((price) => price * 0.9),
);
// { Laptop: 899.1, Phone: 629.1, Tablet: 449.1 }
import { pipe } from "@oofp/core/pipe";
import * as O from "@oofp/core/object";
const env = {
APP_DB_HOST: "localhost",
APP_DB_PORT: "5432",
APP_CACHE_TTL: "3600",
OTHER_VAR: "ignore",
};
const appConfig = pipe(
env,
O.filter((_, key) => key.startsWith("APP_")),
O.mapKeys((key) => key.replace("APP_", "").toLowerCase()),
);
// { db_host: "localhost", db_port: "5432", cache_ttl: "3600" }
import { pipe } from "@oofp/core/pipe";
import * as O from "@oofp/core/object";
const formData = {
name: "Alice",
email: "alice@example.com",
bio: "",
};
const allFilled = pipe(
formData,
O.every((value) => value.length > 0),
);
// false — bio is empty