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.

1,665 lines (1,630 loc) 50.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var mingo = require('mingo'); var sift = require('sift'); function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _createForOfIteratorHelperLoose(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (t) return (t = t.call(r)).next.bind(t); if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var o = 0; return function () { return o >= r.length ? { done: !0 } : { done: !1, value: r[o++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } /** * @description * Use this helper to retrieve the record at the specified index, or null if out of bounds. */ var at = function at(options) { var _records$at; var index = options.index, records = options.records; return (_records$at = records.at(index)) != null ? _records$at : null; }; /** * @description * Use this helper to get the first record in the record set, or null if the record set is empty. */ var first = function first(options) { var _records$; var records = options.records; return (_records$ = records[0]) != null ? _records$ : null; }; /** * @description * Use this helper to get the last record in the record set, or null if the record set is empty. */ var last = function last(options) { var _records; var records = options.records; return (_records = records[records.length - 1]) != null ? _records : null; }; /** * @description * Use this helper to skip the first `count` records. */ var skip = function skip(options) { var count = options.count, records = options.records; if (count <= 0) { return records; } return records.slice(count); }; /** * @description * Use this helper to take at most `count` records from the start of the record set. */ var limit = function limit(options) { var count = options.count, records = options.records; if (count < 0) { return []; } if (count === 0) { return []; } return records.slice(0, count); }; /** * @description * Use this helper to omit the specified fields from each record, returning a new array of records without those keys. */ var omit = function omit(options) { var fields = options.fields, records = options.records; var omitted = []; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; var omitObj = _extends({}, record); for (var _iterator2 = _createForOfIteratorHelperLoose(fields), _step2; !(_step2 = _iterator2()).done;) { var field = _step2.value; if (field in omitObj) { // @ts-expect-error delete omitObj[field]; } } omitted.push(omitObj); } return omitted; }; /** * @description * Use this helper to return an array containing the records corresponding to the given page number (1-based) and page size. * * This helper 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. */ var page = function page(options) { var pageNumber = options.pageNumber, pageSize = options.pageSize, records = options.records; if (pageNumber < 1 || pageSize < 1) { return []; } var startIndex = (pageNumber - 1) * pageSize; return limit({ count: pageSize, records: skip({ count: startIndex, records: records }) }); }; /** * @description * Use this helper to find all the matching records given a query. * * Falls back to the same set of records if no `query` is provided. */ var find = function find(options) { var query = options.query, records = options.records; if (!query) { return records; } return records.filter(sift(query)); }; /** * @description * Use this helper 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. */ var findOne = function findOne(options) { var _records$find; var query = options.query, records = options.records; if (!query) { return first({ records: records }); } return (_records$find = records.find(sift(query))) != null ? _records$find : null; }; /** * @description * Use this helper to count the number of records matching the query. */ var count = function count(options) { var query = options.query, records = options.records; if (!query) { return records.length; } return records.filter(sift(query)).length; }; /** * @description * Use this method to check if any record exists matching the given query. */ var exists = function exists(options) { var query = options.query, records = options.records; if (!query) { return records.length > 0; } return records.some(sift(query)); }; /** * @description * Use this helper to get distinct values of a field among records matching the query. */ var distinct = function distinct(options) { var field = options.field, query = options.query, records = options.records; var filteredItems = query ? records.filter(sift(query)) : records; var seen = new Set(); var result = []; for (var _iterator = _createForOfIteratorHelperLoose(filteredItems), _step; !(_step = _iterator()).done;) { var record = _step.value; var value = record[field]; if (!seen.has(value)) { seen.add(value); result.push(value); } } return result; }; /** * @description * Use this helper to transform all records in the record set and return a new RecordSet of the transformed records. */ var map = function map(options) { var transformer = options.transformer, records = options.records; var transformed = []; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; transformed.push(transformer(record)); } return transformed; }; /** * @description * Use this method to map each record to zero or more records, then flatten the results into a single new RecordSet. * * This is handy for extracting nested arrays or expanding items. */ var flatMap = function flatMap(options) { var transformer = options.transformer, records = options.records; var flatted = []; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; var mapped = transformer(record); for (var _iterator2 = _createForOfIteratorHelperLoose(mapped), _step2; !(_step2 = _iterator2()).done;) { var subItem = _step2.value; flatted.push(subItem); } } return flatted; }; /** * @description * Use this helper to reduce the record set to a single accumulated value. */ var reduce = function reduce(options) { var reducer = options.reducer, initialValue = options.initialValue, records = options.records; var accumulator = initialValue; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; accumulator = reducer(accumulator, record); } return accumulator; }; /** * @description * Use this helper to extract an array of a single field's values from all records in the record set. */ var pluck = function pluck(options) { var field = options.field, records = options.records; var plucked = []; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; plucked.push(record[field]); } return plucked; }; /** * @method * @description * Use this method to pick only the specified fields from each record, returning a new array of records with only those keys. */ var pick = function pick(options) { var fields = options.fields, records = options.records; var picked = []; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; var pickObj = {}; for (var _iterator2 = _createForOfIteratorHelperLoose(fields), _step2; !(_step2 = _iterator2()).done;) { var field = _step2.value; // @ts-expect-error if (field in record) { pickObj[field] = record[field]; } } picked.push(pickObj); } return picked; }; /** * @description * Use this helper 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`). */ var select = function select(options) { var spec = options.spec, records = options.records; var includes = []; var excludes = []; if (typeof spec === 'object' && !Array.isArray(spec)) { var keys = Object.keys(spec); var isInclusive = keys.some(function (k) { return spec[k] === 1; }); if (isInclusive) { includes = keys.filter(function (k) { return spec[k] === 1; }); } else { excludes = keys.filter(function (k) { return spec[k] === 0; }); } } else { /** * Spec is String or Array */ var tokens = typeof spec === 'string' ? spec.split(/\s+/).filter(Boolean) : spec; for (var _iterator = _createForOfIteratorHelperLoose(tokens), _step; !(_step = _iterator()).done;) { var t = _step.value; if (t.startsWith('-')) { excludes.push(t.slice(1)); } else if (t.startsWith('+')) { includes.push(t.slice(1)); } else { includes.push(t); } } } if (includes.length > 0) { return pick({ fields: includes, records: records }); } return omit({ fields: excludes, records: records }); }; /** * @description * Use this helper to sort the records with the provided compare function. */ var sort = function sort(options) { var compareFn = options.compareFn, records = options.records; return [].concat(records).sort(compareFn); }; /** * @description * Use this helper to sort the records by key(s) in ascending or descending order. */ var sortBy = function sortBy(options) { var iteratees = options.iteratees, orders = options.orders, records = options.records; var keys = Array.isArray(iteratees) ? iteratees : [iteratees]; var ords = orders === undefined ? keys.map(function () { return 'asc'; }) : Array.isArray(orders) ? orders : [orders]; return [].concat(records).sort(function (a, b) { for (var i = 0; i < keys.length; i++) { var _ords$i; var key = keys[i]; var order = (_ords$i = ords[i]) != null ? _ords$i : 'asc'; var valA = a[key]; var valB = b[key]; if (valA === valB) continue; var dir = order === 'asc' ? 1 : -1; if (typeof valA === 'string' && typeof valB === 'string') { return valA.localeCompare(valB) * dir; } return (valA < valB ? -1 : 1) * dir; } return 0; }); }; /** * @description * Use helper 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. */ var groupBy = function groupBy(options) { var keyExtractor = options.keyExtractor, records = options.records; var map = new Map(); for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; var key = keyExtractor(record); var group = map.get(key); if (group) { group.push(record); } else { map.set(key, [record]); } } return map; }; /** * @description * Use this method to reverse the order of the records. */ var reverse = function reverse(options) { var records = options.records; return [].concat(records).reverse(); }; /** * @description * Use this method to check if every record matches the given query. */ var every = function every(options) { var query = options.query, records = options.records; return records.every(sift(query)); }; /** * @description * Use this method to check if none of the record matches the given query. */ var none = function none(options) { var query = options.query, records = options.records; return !records.some(sift(query)); }; /** * @method * @description * 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()`. */ var slice = function slice(options) { return options.records.slice(options.start, options.end); }; /** * @description * Use this helper to return the index of the first element in the array where predicate is true, and -1 otherwise. */ var findIndex = function findIndex(options) { var query = options.query, records = options.records; return records.findIndex(sift(query)); }; /** * @description * Use this helper 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. */ var add = function add(options) { var inputNewRecords = options.newRecords, index = options.index, records = options.records; var newrecords = Array.isArray(inputNewRecords) ? inputNewRecords : [inputNewRecords]; var insertIndex = typeof index === 'number' && index >= 0 && index <= records.length ? index : records.length; var updated = [].concat(records.slice(0, insertIndex), newrecords, records.slice(insertIndex)); return updated; }; /** * @description * Use this helper to update records in the record set matching the sift query by merging the provided update object. * * Performs a shallow merge of the update object into matching records. */ var update = function update(options) { var query = options.query, records = options.records, changes = options.changes; var match = sift(query); var updated = []; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; if (match(record)) { updated.push(_extends({}, record, changes)); } else { updated.push(record); } } return updated; }; /** * @description * Update this helper to update record in the record set matching the sift query by merging the provided update object. * * Performs a shallow merge of the update object into the first matching record. */ var updateOne = function updateOne(options) { var query = options.query, records = options.records, changes = options.changes; var match = sift(query); var updated = [].concat(records); var index = 0; for (var _iterator = _createForOfIteratorHelperLoose(updated), _step; !(_step = _iterator()).done;) { var record = _step.value; if (match(record)) { updated[index] = _extends({}, record, changes); break; } index++; } return updated; }; /** * @description * Use this helper to remove all records from the record set that match the given query. */ var remove = function remove(options) { var query = options.query, records = options.records; var match = sift(query); var filtered = []; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; if (!match(record)) { filtered.push(record); } } return filtered; }; /** * @description * Use this helper to remove the first record from the record set that matches the given query. */ var removeOne = function removeOne(options) { var query = options.query, records = options.records; var match = sift(query); var filtered = []; var removed = false; for (var _iterator = _createForOfIteratorHelperLoose(records), _step; !(_step = _iterator()).done;) { var record = _step.value; if (!removed && match(record)) { removed = true; continue; } filtered.push(record); } return filtered; }; var RecordSet = /*#__PURE__*/function () { function RecordSet(records) { if (records === void 0) { records = []; } this.records = void 0; if (!Array.isArray(records)) { throw new Error("A record set records must be an array. You are seeing this error because a type of \"" + typeof records + "\" was passed to the record set constructor."); } this.records = records || []; var propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(this)); for (var _iterator = _createForOfIteratorHelperLoose(propertyNames), _step; !(_step = _iterator()).done;) { var name = _step.value; var value = this[name]; if (name !== 'constructor' && typeof value === 'function') { // @ts-expect-error this[name] = value.bind(this); } } } RecordSet.of = function of(records) { return new RecordSet(records); }; RecordSet.empty = function empty() { return new RecordSet(); }; var _proto = RecordSet.prototype; _proto[Symbol.iterator] = function () { return this.records[Symbol.iterator](); } /** * @method * @description * Use this method to get a shallow-copied array of all records in the record set. * * @example * type Item = { id: number }; * * const record = RecordSet.of<Item>([{ id: 1 }, { id: 2 }]); * * record.all(); // [{ id: 1 }, { id: 2 }] */; _proto.all = function all() { return [].concat(this.records); } /** * @method * @description * Use this method to retrieve the record at the specified index, or null if out of bounds. * * @param index The zero-based index of the desired code unit. A negative index will count back from the last item. * * @example * type Item = { id: number }; * * const record = RecordSet.of<Type>([{ id: 1 }, { id: 2 }]); * * record.at(0); // { id: 1 } * * record.at(5); // null */; _proto.at = function at$1(index) { return at({ index: index, records: this.records }); } /** * @method * @description * Use this method to get the first record in the record set, or null if the record set is empty. * * @example * type Item = { id: number }; * * const record = RecordSet.of<Item>([{ id: 1 }, { id: 2 }]); * * record.first(); // { id: 1 } * * RecordSet.of([]).first(); // null */; _proto.first = function first$1() { return first({ records: this.records }); } /** * @method * @description * Use this method to get the last record in the record set, or null if the record set is empty. * * @example * type Item = { id: number }; * * const record = RecordSet.of<Item>([{ id: 1 }, { id: 2 }]); * * record.last(); // { id: 2 } * * RecordSet.of([]).last(); // null */; _proto.last = function last$1() { return last({ records: this.records }); } /** * @method * @description * Use this method to skip the first `count` records. * * @param count The number of records that should be skipped. * * @example * 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 }] */; _proto.skip = function skip$1(count) { return new RecordSet(skip({ count: count, records: this.records })); } /** * @method * @description * Use this method to take at most `count` records from the start of the record set. * * @param count The max. number of records the record set should hold. * * @example * 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 }] */; _proto.limit = function limit$1(count) { return new RecordSet(limit({ count: count, records: this.records })); } /** * @method * @description * 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. * * @param pageNumber The page number to retrieve, starting at 1. * @param pageSize The number of records per page. * * @example * 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 }] */; _proto.page = function page$1(pageNumber, pageSize) { return new RecordSet(page({ pageNumber: pageNumber, pageSize: pageSize, records: this.records })); } /** * @method * @description * Use this method to get the number of records in the record set. * * @example * type Item = { id: number }; * * const record = RecordSet.of<Item>([{ id: 1 }, { id: 2 }, { id: 3 }]); * * record.length(); // 3 */; _proto.length = function length() { return this.records.length; } /** * @method * @description * Use this method to determine whether the record set contains any records. * * @example * RecordSet.of([]).isEmpty(); // true * * RecordSet.of([1]).isEmpty(); // false */; _proto.isEmpty = function isEmpty() { return this.records.length === 0; } /** * @method * @description * 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. * * @param query The query that should be used to determine to match the records. * * @example * 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 }, * ]); * * // Find all people named 'Bob' * const bobs = people.find({ name: 'Bob' }); * console.log(bobs.all()); // [{ id: 2, name: 'Bob', age: 25 }] * * // Calling find without a query returns the full record set * const all = people.find(); * console.log(all.all()); // same as people.all() */; _proto.find = function find$1(query) { return RecordSet.of(find({ query: query, records: this.records })); } /** * @method * @description * Use this helper to return the index of the first element in the array where predicate is true, and -1 otherwise. * * @param query The query that should be used to determine to match the records. * * @example * 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 index = people.find({ name: 'Bob' }); * console.log(index); // 1 */; _proto.findIndex = function findIndex$1(query) { return findIndex({ query: query, records: this.records }); } /** * @method * @description * 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. * * @param query The query that should be used to determine to match the record. * * @example * 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 } */; _proto.findOne = function findOne$1(query) { return findOne({ query: query, records: this.records }); } /** * @method * @description * Use this method to count the number of records matching the query. * * @param query The query that should be used to determine to match the records. * * @example * type Item = { category: string; value: number }; * * const items = RecordSet.of<Item>([ * { category: 'fruit', value: 10 }, * { category: 'fruit', value: 20 }, * { category: 'vegetable', value: 15 }, * ]); * * const fruitCount = items.count({ category: 'fruit' }); * console.log(fruitCount); // 2 * * const totalCount = items.count(); * console.log(totalCount); // 3 */; _proto.count = function count$1(query) { return count({ query: query, records: this.records }); } /** * @method * @description * Use this method to check if any record exists matching the query. * * @param query The query that should be used to determine to match the records. * * @example * type Item = { type: string; available: boolean }; * * const items = RecordSet.of<Item>([ * { type: 'book', available: true }, * ]); * * const hasBook = items.exists({ type: 'book' }); * console.log(hasBook); // true * * const hasMagazine = items.exists({ type: 'magazine' }); * console.log(hasMagazine); // false * * const hasAny = items.exists(); * console.log(hasAny); // true, because record set is not empty */; _proto.exists = function exists$1(query) { return exists({ query: query, records: this.records }); } /** * @method * @description * Use this method to check if every record matches the given query. * * @param query The query that should be used to determine to match the records. * * @example * type Item = { type: string; available: boolean }; * * const items = RecordSet.of<Item>([ * { type: 'book', available: true }, * { type: 'food', available: true }, * ]); * * const isEverythingAvailable = items.every({ available: true }); * console.log(isEverythingAvailable); // true */; _proto.every = function every$1(query) { return every({ query: query, records: this.records }); } /** * @method * @description * Use this method to check if none record matches the given query. * * @param query The query that should be used to determine to match the records. * * @example * type Item = { type: string; available: boolean }; * * const items = RecordSet.of<Item>([ * { type: 'book', available: false }, * { type: 'food', available: false }, * ]); * * const noneAvailable = items.every({ available: true }); * console.log(noneAvailable); // true */; _proto.none = function none$1(query) { return none({ query: query, records: this.records }); } /** * @method * @description * Use this method to get distinct values of a field among records matching the query. * * @param field The field that needs to be inspected. * @param query The query that should be used to determine to match the records. * * @example * 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'] */; _proto.distinct = function distinct$1(field, query) { return distinct({ field: field, query: query, records: this.records }); } /** * @method * @description * Use this helper to perform the specified action for each record in the record set. * * @param fn The function to execute on each element, receiving the record, the index, and the original array. * * @example * type Item = { id: number }; * * const records = RecordSet.of([{ id: 1 }, { id: 2 }, { id: 3 }]); * * records.forEach((record, index) => { * console.log(record, index); * }); */; _proto.forEach = function forEach(fn) { this.records.forEach(fn); } /** * @method * @description * Use this method to transform all records in the record set and return a new RecordSet of the transformed records. * * @param fn The function will transform the record into a `TMappedRecord` shape. * * @example * 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 }, * ]); * * // Create a record set of user names * const userNames = users.map(user => { * return user.name; * }); * * console.log(userNames.all()); // ['Alice', 'Bob'] */; _proto.map = function map$1(fn) { return new RecordSet(map({ transformer: fn, records: this.records })); } /** * @method * @description * Use this method to map each record to zero or more records, then flatten the results into a single new RecordSet. * * This is handy for extracting nested arrays or expanding items. * * @param fn The function will transform the array of records into a `TMappedRecord` shape. * * @example * 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; * }); * * comments.all(); // [{ id: 101, text: 'a' }, { id: 102, text: 'b' }, { id: 103, text: 'c' }] */; _proto.flatMap = function flatMap$1(fn) { return new RecordSet(flatMap({ transformer: fn, records: this.records })); } /** * @method * @description * Use this method to create a shallow copy slice of the records in the record set, extracting records from the specified range. * * It operates on the current set of records without applying any filtering. * * For filtering, use `.find()` prior to `.slice()`. * * @param start The beginning index of the specified portion of the array. * If start is undefined, then the slice begins at index 0. * @param end The end index of the specified portion of the array. This is exclusive of the element at the index 'end'. * If end is undefined, then the slice extends to the end of the array. * * @example * 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 sliced = records.slice({ start: 1, end: 3 }); * sliced.all(); // [{ id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 35 }] * * const slicedFromEnd = records.slice({ start: -3, end: -1 }); * slicedFromEnd.all(); // [{ id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }] */; _proto.slice = function slice$1(start, end) { return new RecordSet(slice({ start: start, end: end, records: this.records })); } /** * @method * @description * Use this method to reduce the record set to a single accumulated value. * * @param fn The function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. * @param initialValue The initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. * * @example * 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 */; _proto.reduce = function reduce$1(fn, initialValue) { return reduce({ reducer: fn, initialValue: initialValue, records: this.records }); } /** * @method * @description * 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. * * @param recordSet The other `RecordSet` to concatenate with. * * @returns A new `RecordSet<TRecord>` containing all records from both sets, in order. * * @example * 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 }] */; _proto.concat = function concat(recordSet) { return new RecordSet([].concat(this.records, recordSet.all())); } /** * @method * @description * Use this method to extract an array of a single field's values from all records in the record set. * * @param field The field that needs to be extracted from the records. * * @example * 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] */; _proto.pluck = function pluck$1(field) { return pluck({ field: field, records: this.records }); } /** * @method * @description * Use this method to pick only the specified fields from each record, returning a new RecordSet of records with only those keys. * * @param fields The array of fields that need to be extracted to create the new shape. * * @example * 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' }, * ]); * * // Pick only id and name fields * const userSummaries = users.pick(['id', 'name']); * * console.log(userSummaries.all()); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }] */; _proto.pick = function pick$1(fields) { return RecordSet.of(pick({ fields: fields, records: this.records })); } /** * @method * @description * Use this method to omit the specified fields from each record, returning a new record set of records without those keys. * * @param omit The fields that need to be ommited to create the new shape. * * @example * 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' }, * ]); * * // Omit the password field for security reasons * const safeUsers = users.omit(['password']); * * console.log(safeUsers.all()); * // [{ id: 1, name: 'Alice', age: 30 }, { id: 2, name: 'Bob', age: 25 }] */; _proto.omit = function omit$1(fields) { return RecordSet.of(omit({ fields: fields, records: this.records })); } /** * @method * @description * 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 }`. * * @param spec The specifications of which fields need to be picked or omitted. * * Inclusive if any field is positively specified (no `-` or `0`). * * Exclusive if only negatives (`-`) or zeros (`0`). * * @example * const users = RecordSet.of([{ id:1, name:'A', age:30 }]); * * users.select('id name').all(); // [{ id: 1, name: 'A' }] * * users.select('-age').all(); // [{ id: 1, name: 'A' }] */; _proto.select = function select$1(spec) { return new RecordSet(select({ spec: spec, records: this.records })); } /** * @method * @description * Use this helper to sort the records with the provided compare function. * * @param sort The function used to determine the order of the elements. It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive value otherwise. * * @example * type Product = { name: string; price: number }; * * const products = RecordSet.of<Product>([ * { name: 'Banana', price: 1.5 }, * { name: 'Apple', price: 2.0 }, * { name: 'Orange', price: 1.2 }, * ]); * * // Sort products by price ascending * const sorted = products.sort((a, b) => { * return a.price - b.price; * }); * * console.log(sorted.pluck('price')); // [1.2, 1.5, 2.0] */; _proto.sort = function sort$1(compareFn) { return new RecordSet(sort({ compareFn: compareFn, records: this.records })); } /** * @method * @description * Use this helper to sort the records by key(s) in ascending or descending order. * * @param iteratees One or more keys to sort by. * @param orders Order(s) for each key; can be a single 'asc' | 'desc' or an array corresponding to iteratees. * * @example * type Product = { category: string; price: number }; * * const products = RecordSet.of<Product>([ * { category: 'fruit', price: 5 }, * { category: 'fruit', price: 3 }, * { category: 'vegetable', price: 4 }, * ]); * * // Sort by price ascending * const sortedByPrice = products.sortBy('price'); * console.log(sortedByPrice.pluck('price')); // [3, 4, 5] * * // Sort by price descending * const sortedByPriceDesc = products.sortBy('price', 'desc'); * console.log(sortedByPriceDesc.pluck('price')); // [5, 4, 3] * * // Sort by category ascending, then price descending * const sortedByCategoryPrice = products.sortBy(['category', 'price'], ['asc', 'desc']); * console.log(sortedByCategoryPrice.all()); * // [ * // { category: 'fruit', price: 5 }, * // { category: 'fruit', price: 3 }, * // { category: 'vegetable', price: 4 } * // ] */; _proto.sortBy = function sortBy$1(iteratees, orders) { return new RecordSet(sortBy({ iteratees: iteratees, orders: orders, records: this.records })); } /** * @method * @description * Use this method to group records by a key derived from each record. * * @param fn The function that will serve as the key extractor. This function will be used to generate the keys in the resulting map. * * It returns a `Map` where keys are group keys and values are record sets of grouped records. * @example * 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; * }); * * grouped.get(25)?.all(); // [{ id: 2, name: 'Bob', age: 25 }, { id: 3, name: 'Eve', age: 25 }] * * grouped.get(30)?.all(); // [{ id: 1, name: 'Alice', age: 30 }] */; _proto.groupBy = function groupBy$1(fn) { var map = groupBy({ keyExtractor: fn, records: this.records }); var recordSetMap = new Map(); for (var _iterator2 = _createForOfIteratorHelperLoose(map), _step2; !(_step2 = _iterator2()).done;) { var _step2$value = _step2.value, key = _step2$value[0], groupRecords = _step2$value[1]; recordSetMap.set(key, new RecordSet(groupRecords)); } return recordSetMap; } /** * @method * @description * Use this method to reverse the order of the records. * * @example * type Item = { id: number; name: string }; * * const items = RecordSet.of<Item>([ * { id: 1, name: 'First' }, * { id: 2, name: 'Second' }, * { id: 3, name: 'Third' }, * ]); * * const reversed = items.reverse(); * * console.log(reversed.all()); * // [ * // { id: 3, name: 'Third' }, * // { id: 2, name: 'Second' }, * // { id: 1, name: 'First' } * // ] */; _proto.reverse = function reverse$1() { return new RecordSet(reverse({ records: this.records })); } /** * @method * @description * Use this helper to create a custom query using the provided condition and options. * * This method returns a `mingo` [Cursor](https://www.npmjs.com/package/mingo), which allows you to further refine and chain query operations as needed. * * Note: Queries are lazily evaluated. Meaning that only when `.all()`, `.next()`, or similar methods are invoked the operations are ran. * * @param condition The query condition object used to define the criteria for matching documents. * @param options Optional configuration settings to customize the query behavior. */; _proto.query = function query(condition, options) { return new mingo.Query(condition, options).find(this.records); } /** * @method * @description * 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. * * @param records A record or an array of records to add. * @param index Optional zero-based index at which to insert the new records. * * @example * type Item = { id: number }; * * const items = RecordSet.of<Item>([{ 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 }] */; _proto.add = function add$1(records, index) { return new RecordSet(add({ newRecords: records, index: index, records: this.records })); } /** * @method * @description * Use this method to add one or more records to the record set at the beggining of the record set. * * @param records A record or an array of records to add. * * @example * 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 }] */; _proto.prepend = function prepend(records) { return new RecordSet(add({ newRecords: records, index: 0, records: this.records })); } /** * @method * @description * Use this method to add one or more records to the record set at the end of the record set. * * @param records A record or an array of records to add. * * @example * 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 }] */; _proto.append = function append(records) { return new RecordSet(add({ newRecords: records, records: this.records })); } /** * @method * @description * 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. * * @param query The query that should be used to determine to match the records. * @param changes The partial object with fields to update in matching records. * * @example * 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 }, * { 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 } * // ] */; _proto.update = function update$1(query, changes) { return new RecordSet(update({ query: query, changes: changes, records: this.records })); } /** * @method * @description * 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. * * @param query The query that should be used to determine to match the record. * @param update The partial object with fields to update in matching records. * * @example * 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 }, * { id: 3, name: 'Eve', age: 25 }, * ]); * * const updated = users.updateOne({ age: 25 }, { age: 26 }); * * console.log(updated.all()); * // [ * // { id: 1, name: 'Alice', age: 30 }, * // { id: 2, name: 'Bob', age: 26 }, // Only first matched record updated * // { id: 3, name: 'Eve', age: 25 } * // ] */; _proto.updateOne = function updateOne$1(query, changes) { return new RecordSet(updateOne({ query: query, changes: changes, records: this.records })); } /** * @method * @description * Use this method to remove all records from the record set that match the given query. * * @param query The query that should be used to determine to match the records. * * @example * type Product = { id: number; name: string; }; * * const items = RecordSet.of<Product>([ * { 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' } * // ] */; _proto.remove = function remove$1(query) { return new RecordSet(remove({ query: query, records: this.records })); } /** * @method * @description * Use this method to remove the first record from the record set that matches the given query. * * @param query The query that should be used to determine to match the records. * * @example * type Product = { id: number; name: string; }; * * const items = RecordSet.of<Product>([ * { id: 1, name: 'Apple' }, *