UNPKG

@future-widget-lab/record-set

Version:

A dedicated data structure for in-memory record collections, offering fluent, immutable APIs for MongoDB-like querying, sorting, and transformation.

947 lines (610 loc) 27.5 kB
# @future-widget-lab/record-set A dedicated data structure for in-memory record collections, offering fluent, immutable APIs for MongoDB-like querying, sorting, and transformation. ## Features - Immutable, read-only operations that always return new collections or primitives. - MongoDB-style querying powered by `sift` for expressive filters like `$gt`, `$lt`, `$in`, and more. - Rich API for retrieval, inspection, filtering, transformation, sorting, and mutation of records. - Supports intuitive iteration with native `for...of` loops and `forEach`. - Includes immutable mutation helpers: `add`, `prepend`, `append`, `update`, `updateOne`, `remove`, `removeOne`, `concat`. - Custom query builder via `mingo` cursor for advanced chainable operations. ## Philosophy The idea of a dedicated data structure for handling records was born from my envy of the ergonomics available in back-end systems. There, developers enjoy standardized, expressive query interfaces via ORMs or ODMs (e.g., `prisma`, `mongoose`). While arrays of objects remain the bread and butter of front-end development, I believe having a dedicated, standardized interface for working with remote data records (e.g., from `@tanstack-query/react`, `swr`) brings clear value. Without it, front-end developers fall back on raw array methods and repeated ad-hoc logic for common tasks. This package provides a data structure to serve as a foundation for managing collections of records in front-end applications: - **Minimal abstraction**: It does not replace arrays or attempt to serve as a general-purpose data structure, but offers a solid foundation before reaching for manual array operations as data complexity grows. - **Ergonomics with immutability**: Immutability is a helpful side effect rather than a goal. The focus is on making common front-end tasks easier—such as transforming records to IDs or querying with multiple criteria—while preventing accidental mutations and side effects. - **Data-agnostic**: Though designed with front-end cases in mind, `RecordSet` works seamlessly with any array of objects regardless of environment or domain. - **Declarative and familiar**: Inspired in `mongoose` and leveraging MongoDB-style queries via `sift`, developers can express complex filters clearly and intuitively. - **Consistent API surface**: One unified interface covers common needs like finding, filtering, counting, mapping, picking fields, and sorting. This `RecordSet` differs from typical back-end record collections in an important way: it intentionally has no opinions in how mutation methods such as adding, updating, or replacing records should sync the state with the back-end. Its sole purpose is to provide a clean, declarative way to query and transform collections. Mutation logic tends to be the most complex part of a system and is heavily tied to the specifics of the framework or state management approach in use (e.g., `react`, `vue`, etc.). By avoiding mutation responsibilities, this package stays focused, predictable, and easy to integrate with any architecture. ## Installation Install with npm: ```bash npm install @future-widget-lab/record-set ``` > **Note**: `sift` and `mingo` are used underneath and _should_ be installed alongside if you wish to leverage their types. ## Usage ### Basics #### `RecordSet.of` Use this static method to create a record set from the given array. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); ``` #### `RecordSet.prototype.all` Use this method to get a shallow-copied array of all records in the record set. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); console.log(records.all()); // [{ id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }] ``` ### Inspection #### `RecordSet.prototype.empty` Use this static method to create an empty record set. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const empty = RecordSet.empty<TPerson>(); ``` #### `RecordSet.prototype.isEmpty` Use this method to determine whether the record set contains any records. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); console.log(records.isEmpty()); // false const emptyRecords = RecordSet.empty<Person>(); console.log(emptyRecords.isEmpty()); // true ``` #### `RecordSet.prototype.length` Use this method to get the number of records in the record set. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); console.log(records.length()); // 2 const emptyRecords = RecordSet.empty<Person>(); console.log(emptyRecords.length()); // 0 ``` #### `RecordSet.prototype.count` Use this method to count the number of records matching the query. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const peopleAboveTwenty = records.count({ age: { $gte: 20 } }); console.log(peopleAboveTwenty); // 1 ``` #### `RecordSet.prototype.exists` Use this method to check if any record exists matching the query. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const existsUnderTwenty = records.exists({ age: { $lt: 20 } }); console.log(existsUnderTwenty); // false ``` #### `RecordSet.prototype.every` Use this method to check if every record matches the given query. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const everyoneIsAboveFifteen = records.exists({ age: { $gte: 15 } }); console.log(everyoneIsAboveFifteen); // true ``` #### `RecordSet.prototype.none` Use this method to check if none record matches the given query. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const noneIsAboveFourty = records.none({ age: { $gte: 40 } }); console.log(noneIsAboveFourty); // true ``` ### Retrieval #### `RecordSet.prototype.at` Use this method to retrieve the record at the specified index, or null if out of bounds. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const first = records.at(0); console.log(first); // { id: 1, name: 'Alice', age: 30 } const first = records.at(1); console.log(first); // { id: 2, name: 'Bob', age: 25 } ``` #### `RecordSet.prototype.first` Use this method to get the first record in the record set, or null if the record set is empty. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const first = records.first(); console.log(first); // { id: 1, name: 'Alice', age: 30 } ``` #### `RecordSet.prototype.last` Use this method to get the last record in the record set, or null if the record set is empty. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const last = records.last(); console.log(last); // { id: 2, name: 'Bob', age: 25 } ``` ### Iteration #### Loops ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); for (const person of records) { console.log(person.id, person.name); } ``` #### `RecordSet.prototype.forEach` ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); records.forEach((record) => { console.log(person.id, person.name); }); ``` ### Filtering #### `RecordSet.prototype.find` Use this method to find all the matching records given a query. Falls back to the same set of records if no `query` is provided. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const people = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }, ]); const peopleNamedBob = people.find({ name: 'Bob' }); console.log(peopleNamedBob.all()); // [{ id: 2, name: 'Bob', age: 25 }] const allPeople = people.find(); console.log(allPeople.all()); // same as people.all() ``` #### `RecordSet.prototype.findOne` Use this method to find the first matching record given a query. Defaults to the first element if no `query` is provided. Fallbacks to `null` if the query provided does not return any matches. ```typescript type Person = { id: number; name: string; age: number }; const people = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const bob = people.findOne({ name: 'Bob' }); console.log(bob); // { id: 2, name: 'Bob', age: 25 } const nonExistent = people.findOne({ name: 'Eve' }); console.log(nonExistent); // null const firstPerson = people.findOne(); console.log(firstPerson); // { id: 1, name: 'Alice', age: 30 } ``` #### `RecordSet.prototype.findIndex` Use this helper to return the index of the first element in the array where predicate is true, and -1 otherwise. ```typescript type Person = { id: number; name: string; age: number }; const people = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }, ]); const bobsIndex = people.findIndex({ name: 'Bob' }); console.log(bobsIndex); // 1 ``` ### Advanced Filtering Sometimes, your filtering and querying needs go beyond simple MongoDB-like queries supported by the core `RecordSet` API. You may require complex operations such as multi-stage aggregations, projections, or sorting combined with filters in sophisticated ways. In these cases, using the `.query()` method is recommended. It leverages [`mingo`](https://www.npmjs.com/package/mingo), a full-featured MongoDB query engine that extends your querying capabilities. The `.query()` method returns a [`mingo` Cursor](https://www.npmjs.com/package/mingo), enabling you to build complex, composable query pipelines with operations like. The queries are lazily evaluated processing occurs only upon invocation of terminal methods such as `.all()` or `.next()`. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; import type { Options } from 'mingo'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }, { id: 4, name: 'Charlie', age: 28 }, { id: 5, name: 'Dave', age: 40 }, ]); // Build a mingo cursor to find persons age 25 or older const cursor = records.query({ age: { $gte: 25 } }).sort({ age: -1 }); // explicitly sort descending by age // Take top 3 records according to the sorting const top3 = cursor.limit(3).all(); console.log(top3); // [{ id: 5, name: 'Dave', age: 40 }, { id: 3, name: 'Eve', age: 35 }, { id: 1, name: 'Alice', age: 30 }] ``` ### Subsetting #### `RecordSet.prototype.skip` Use this method to skip the first `count` records. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number }; const records = RecordSet.of<Item>([ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }, ]); const skipped = records.skip(2); console.log(skipped.all()); // [{ id: 3 }, { id: 4 }, { id: 5 }] ``` #### `RecordSet.prototype.limit` Use this method to take at most `count` records from the start of the record set. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number }; const records = RecordSet.of<Item>([ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }, ]); const limited = records.limit(3); console.log(limited.all()); // [{ id: 1 }, { id: 2 }, { id: 3 }] ``` #### `RecordSet.prototype.page` Use this method to return a record set containing the records corresponding to the given page number (1-based) and page size. This method calculates the starting index by `(pageNumber - 1) * pageSize`, then skips that many records, and finally limits the result to `pageSize` number of records. If either `pageNumber` or `pageSize` is less than 1, this method returns an empty RecordSet. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number }; const records = RecordSet.of<Item>([ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }, { id: 7 }, { id: 8 }, { id: 9 }, { id: 10 }, ]); const firstPage = records.page(1, 3); console.log(firstPage.all()); // [{ id: 1 }, { id: 2 }, { id: 3 }] const secondPage = records.page(2, 3); console.log(secondPage.all()); // [{ id: 4 }, { id: 5 }, { id: 6 }] ``` #### `RecordSet.prototype.slice` Use this method to create a shallow copy slice of the records in the record set, extracting records from the specified `start` index up to, but not including, the `end` index. It operates on the current set of records without applying any filtering. For filtering, use `.find()` prior to `.slice()`. ```typescript const records = RecordSet.of([1, 2, 3, 4, 5]); const sliced = records.slice(1, 3); console.log(sliced.all()); // [2, 3] const slicedFromEnd = records.slice(-3, -1); console.log(slicedFromEnd.all()); // [3, 4] ``` ### Ordering #### `RecordSet.prototype.sort` Use this method to sort the records with the provided compare function. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }, ]); const sorted = records.sort((a, b) => { return a.age - b.age; }); console.log(sorted.all()); // [{ id: 2, name: 'Bob', age: 25 }, { id: 1, name: 'Alice', age: 30 }, { id: 3, name: 'Eve', age: 35 }] ``` #### `RecordSet.prototype.sortBy` Use this helper to sort the records by key(s) in ascending or descending order. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }, ]); const multiSorted = records.sortBy(['name', 'age'], ['asc', 'desc']); console.log(multiSorted.all()); // sorted by name ascending, age descending ``` #### `RecordSet.prototype.reverse` Use this method to reverse the order of the records. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const records = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }, ]); const reversed = records.reverse(); console.log(reversed.all()); // [{ id: 3, name: 'Eve', age: 35 }, { id: 2, name: 'Bob', age: 25 }, { id: 1, name: 'Alice', age: 30 }] ``` ### Transforming #### `RecordSet.prototype.map` Use this method to transform all records in the record set and return a new RecordSet of the transformed records. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type User = { id: number; name: string; age: number }; const users = RecordSet.of<User>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, ]); const userNames = users.map((user) => { return user.name; }); console.log(userNames.all()); // ['Alice', 'Bob'] ``` #### `RecordSet.prototype.flatMap` Use this method to map each record to zero or more records, then flatten the results into a single new record set. This is handy for extracting nested arrays or expanding items. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Comment = { id: number; text: string }; type Post = { id: number; comments: Array<Comment> }; const posts = RecordSet.of<Post>([ { id: 1, comments: [ { id: 101, text: 'a' }, { id: 102, text: 'b' }, ], }, { id: 2, comments: [{ id: 103, text: 'c' }] }, ]); const comments = posts.flatMap((post) => { return post.comments; }); console.log(comments.all()); // [{ id: 101, text: 'a' }, { id: 102, text: 'b' }, { id: 103, text: 'c' }] ``` #### `RecordSet.prototype.pluck` Use this method to extract an array of a single field's values from all records in the record set. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const people = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }, ]); const ages = people.pluck('age'); console.log(ages); // [30, 25, 35] ``` #### `RecordSet.prototype.pick` Use this method to pick only the specified fields from each record, returning a new RecordSet of records with only those keys. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type User = { id: number; name: string; age: number; email: string }; const users = RecordSet.of<User>([ { id: 1, name: 'Alice', age: 30, email: 'alice@example.com' }, { id: 2, name: 'Bob', age: 25, email: 'bob@example.com' }, ]); const userSummaries = users.pick(['id', 'name']); console.log(userSummaries.all()); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }] ``` #### `RecordSet.prototype.omit` Use this method to omit the specified fields from each record, returning a new record set of records without those keys. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type User = { id: number; name: string; age: number; password: string }; const users = RecordSet.of<User>([ { id: 1, name: 'Alice', age: 30, password: 'secret1' }, { id: 2, name: 'Bob', age: 25, password: 'secret2' }, ]); const safeUsers = users.omit(['password']); console.log(safeUsers.all()); // [{ id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }] ``` #### `RecordSet.prototype.select` Use this method to project each record to include or exclude fields, returning a new record set of records with only those keys: - String: `'a b -c +d'`. - Array of strings: `['a', '-b']`. - Object notation: `{ a: 1, b: 1 }` or `{ c: 0 }`. Inclusive if any field is positively specified (no `-` or `0`). Exclusive if only negatives (`-`) or zeros (`0`). ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number; name: string; age: number }; const users = RecordSet.of<Item>([{ id: 1, name: 'Bob', age: 30 }]); users.select('id name').all(); // [{ id: 1, name: 'Bob' }] users.select('-age').all(); // [{ id: 1, name: 'Bob' }] ``` #### `RecordSet.prototype.concat` Use this method to concatenate the current record set with another record set and returns a new combined record set. This method does not modify the original record sets but creates a new one containing all records of both. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number }; const set1 = RecordSet.of<Item>([{ id: 1 }, { id: 2 }]); const set2 = RecordSet.of<Item>([{ id: 3 }, { id: 4 }]); const combined = set1.concat(set2); console.log(combined.all()); // [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }] ``` #### `RecordSet.prototype.groupBy` Use this method to group records by a key derived from each record. It returns a `Map` where keys are group keys and values are record sets of grouped records. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const people = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 25 }, ]); const grouped = people.groupBy((person) => { return person.age; }); console.log(grouped.get(25)?.all()); // [{ id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 25 }] console.log(grouped.get(30)?.all()); // [{ id: 1, name: 'Alice', age: 30 }] ``` #### `Recordset.prototype.distinct` Use this method to get distinct values of a field among records matching the query. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Product = { category: string; name: string }; const products = RecordSet.of<Product>([ { category: 'fruit', name: 'apple' }, { category: 'vegetable', name: 'carrot' }, { category: 'fruit', name: 'banana' }, ]); const categories = products.distinct('category'); console.log(categories); // ['fruit', 'vegetable'] const fruitNames = products.distinct('name', { category: 'fruit' }); console.log(fruitNames); // ['apple', 'banana'] ``` #### `RecordSet.prototype.reduce` Use this method to reduce the record set to a single accumulated value. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { name: string; age: number }; const people = RecordSet.of<Person>([ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Eve', age: 35 }, ]); const totalAge = people.reduce((acc, person) => { return acc + person.age; }, 0); console.log(totalAge); // 90 ``` ### Mutations #### `RecordSet.prototype.add` Use this method to add one or more records to the record set at the specified index. The insertion index is zero-based. If omitted or out of bounds, new records are appended at the end. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number }; const items = RecordSet.of([{ id: 1 }, { id: 2 }, { id: 3 }]); // Insert at index 1 const updated = items.add({ id: 99 }, 1); console.log(updated.all()); // [{ id: 1 }, { id: 99 }, { id: 2 }, { id: 3 }] // Append by default const appended = items.add([{ id: 4 }, { id: 5 }]); console.log(appended.all()); // [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }] ``` #### `RecordSet.prototype.prepend` Use this method to add one or more records to the record set at the beggining of the record set. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number }; const items = RecordSet.of<Item>([{ id: 1 }, { id: 2 }, { id: 3 }]); const prepended = items.prepend([{ id: 4 }, { id: 5 }]); console.log(prepended.all()); // [{ id: 4 }, { id: 5 }, { id: 1 }, { id: 2 }, { id: 3 }] ``` #### `RecordSet.prototype.append` Use this method to add one or more records to the record set at the end of the record set. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Item = { id: number }; const items = RecordSet.of<Item>([{ id: 1 }, { id: 2 }, { id: 3 }]); const appended = items.append([{ id: 4 }, { id: 5 }]); console.log(appended.all()); // [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }] ``` #### `RecordSet.prototype.update` Use this method to update records in the record set matching the given query by merging the provided update object. Performs a shallow merge of the update object into matching records. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const users = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 25 }, ]); const updated = users.update({ age: 25 }, { age: 26 }); console.log(updated.all()); // [{ id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 26 }, { id: 3, name: 'Eve', age: 26 }] ``` #### `RecordSet.prototype.updateOne` Use this method to update the first record in the record set matching the given query by merging the provided update object. Performs a shallow merge of the update object into the first matching record. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const users = RecordSet.of<Person>([ { id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 25 }, ]); const updated = users.updateOne({ age: 25 }, { age: 26 }); // Only first matched record updated console.log(updated.all()); // [{ id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 26 }, { id: 3, name: 'Eve', age: 25 }] ``` #### `RecordSet.prototype.remove` Use this method to remove all records from the record set that match the given query. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const items = RecordSet.of<Person>([ { id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 3, name: 'Banana' }, { id: 4, name: 'Cherry' }, ]); // Remove all items with name 'Banana' const updated = items.remove({ name: 'Banana' }); console.log(updated.all()); // [{ id: 1, name: 'Apple' }, { id: 4, name: 'Cherry' }] ``` #### `RecordSet.prototype.removeOne` Use this method to remove the first record from the record set that matches the given query. ```typescript import { RecordSet } from '@future-widget-lab/record-set'; type Person = { id: number; name: string; age: number }; const items = RecordSet.of<Person>([ { id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 3, name: 'Banana' }, { id: 4, name: 'Cherry' }, ]); // Remove the first item with name 'Banana' const updated = items.removeOne({ name: 'Banana' }); console.log(updated.all()); // [{ id: 1, name: 'Apple' }, { id: 3, name: 'Banana' }, { id: 4, name: 'Cherry' }] ``` ## Additional Resources - [`sift` documentation](https://www.npmjs.com/package/sift) - [`mingo` documentation](https://www.npmjs.com/package/mingo) ## License MIT