Data Prism Utils
Common utility functions used across the Data Prism ecosystem. This package provides lightweight helper functions for functional programming patterns, data transformation, and common operations that are shared between Data Prism packages.
Overview
Data Prism Utils is built around several key principles:
- Functional: Pure functions with predictable behavior and no side effects
- Type-safe: Comprehensive TypeScript support with generic type definitions
- Null-safe: Graceful handling of null and undefined values
- Minimal: Small, focused utilities without external dependencies
Installation
npm install @data-prism/utils
Core Concepts
Utility Functions
The package provides utility functions that handle common patterns in data processing:
- Mapping: Apply functions to single items or arrays uniformly
- Piping: Chain operations together in a functional style
- Null handling: Safe operations that gracefully handle null/undefined values
API Reference
applyOrMap(itemItemsOrNull, fn)
Applies a function to an item or maps it over an array of items. Handles null and undefined gracefully by returning them unchanged.
Parameters:
itemItemsOrNull (T | T[] | null | undefined) - Single item, array of items, or null/undefined
fn (Function) - Function to apply to each item
Returns: Result of applying fn to the item(s), or null/undefined if input was null/undefined
import { applyOrMap } from "@data-prism/utils";
applyOrMap(5, x => x * 2);
applyOrMap([1, 2, 3], x => x * 2);
applyOrMap(null, x => x * 2);
applyOrMap(undefined, x => x * 2);
applyOrMapAsync(itemItemsOrNull, asyncFn)
Applies an async function to an item or maps it over an array of items. Handles null and undefined gracefully.
Parameters:
itemItemsOrNull (T | T[] | null | undefined) - Single item, array of items, or null/undefined
asyncFn (Function) - Async function to apply to each item
Returns: Promise resolving to the result of applying asyncFn to the item(s), or null/undefined if input was null/undefined
import { applyOrMapAsync } from "@data-prism/utils";
await applyOrMapAsync(5, async x => x * 2);
await applyOrMapAsync([1, 2, 3], async x => x * 2);
applyOrMapAsync(null, async x => x * 2);
applyOrMapAsync(undefined, async x => x * 2);
pipeThru(init, fns)
Pipes a value through a series of functions in sequence. Each function receives the result of the previous function.
Parameters:
init (T) - Initial value to pipe through the functions
fns (Function[]) - Array of functions to pipe the value through
Returns: The result after applying all functions in sequence
import { pipeThru } from "@data-prism/utils";
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const toString = x => x.toString();
pipeThru(10, [add5, multiply2, toString]);
Examples
Data Transformation
import { applyOrMap, pipeThru } from "@data-prism/utils";
const data = [{ name: "John" }, { name: "Jane" }];
const addGreeting = person => ({ ...person, greeting: `Hello, ${person.name}` });
const toUpperCase = person => ({ ...person, name: person.name.toUpperCase() });
const result = applyOrMap(data, person =>
pipeThru(person, [addGreeting, toUpperCase])
);
console.log(result);
Async Data Processing
import { applyOrMapAsync } from "@data-prism/utils";
async function fetchUserDetails(userId) {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
const userIds = ["user-1", "user-2", "user-3"];
const userDetails = await applyOrMapAsync(userIds, fetchUserDetails);
const singleUser = await applyOrMapAsync("user-1", fetchUserDetails);
Pipeline Processing
import { pipeThru } from "@data-prism/utils";
const parseJson = str => JSON.parse(str);
const extractUsers = data => data.users;
const filterActive = users => users.filter(u => u.active);
const sortByName = users => users.sort((a, b) => a.name.localeCompare(b.name));
const rawData = '{"users": [{"name": "Bob", "active": true}, {"name": "Alice", "active": false}]}';
const result = pipeThru(rawData, [
parseJson,
extractUsers,
filterActive,
sortByName
]);
console.log(result);
Null-Safe Operations
import { applyOrMap } from "@data-prism/utils";
function processUserData(userData) {
return applyOrMap(userData, user => ({
...user,
displayName: user.firstName + " " + user.lastName
}));
}
processUserData(null);
processUserData(undefined);
processUserData(user);
processUserData([user1, user2]);
Resource Processing in Data Prism
import { applyOrMap, pipeThru } from "@data-prism/utils";
function normalizeResources(resources) {
const addType = resource => ({ ...resource, type: resource.type || "unknown" });
const addId = resource => ({ ...resource, id: resource.id || generateId() });
const validateRequired = resource => {
if (!resource.attributes) throw new Error("Missing attributes");
return resource;
};
return applyOrMap(resources, resource =>
pipeThru(resource, [addType, addId, validateRequired])
);
}
const singleResource = { attributes: { name: "Test" } };
const multipleResources = [
{ attributes: { name: "Test 1" } },
{ attributes: { name: "Test 2" } }
];
const normalizedSingle = normalizeResources(singleResource);
const normalizedMultiple = normalizeResources(multipleResources);
TypeScript Support
Data Prism Utils includes comprehensive TypeScript definitions:
import { applyOrMap, applyOrMapAsync, pipeThru } from "@data-prism/utils";
const numbers: number[] = [1, 2, 3];
const doubled: number[] = applyOrMap(numbers, x => x * 2);
const asyncResult: Promise<string[]> = applyOrMapAsync(
["a", "b", "c"],
async (str: string): Promise<string> => str.toUpperCase()
);
const result: string = pipeThru(
42,
[
(x: number) => x * 2,
(x: number) => x.toString(),
(x: string) => x.padStart(4, '0')
]
);
Performance Considerations
Functional Programming Benefits
- Immutability: Functions don't modify input data, reducing bugs
- Composability: Small functions can be combined in different ways
- Testability: Pure functions are easy to test in isolation
- Predictability: Same inputs always produce same outputs
Memory Efficiency
- No dependencies: Minimal memory footprint
- Small functions: Only include what you need
- Lazy evaluation: Operations are only performed when needed
Usage Patterns
const result = pipeThru(data, [transform1, transform2, transform3]);
const step1 = transform1(data);
const step2 = transform2(step1);
const step3 = transform3(step2);
Related Packages
@data-prism/core - Uses utils for resource and query processing
@data-prism/memory-store - Uses utils for data transformations
@data-prism/postgres-store - Uses utils for SQL query building