@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
JavaScript
'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' },
*