tsbase
Version:
Base class libraries for TypeScript
248 lines • 9.92 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Queryable = void 0;
/* eslint-disable max-lines */
const Regex_1 = require("../../System/Regex");
const Strings_1 = require("../../System/Strings");
const module_1 = require("../../Utility/Logger/module");
class Queryable extends Array {
constructor() { super(); }
static From(items) {
return Object.create(Queryable.prototype, Object.getOwnPropertyDescriptors(items));
}
/**
* Returns a copy of the array shuffled based on the knuth shuffle algorithm
*/
Shuffle() {
return this.mutableArrayQuery((array) => {
let currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return Queryable.From(array);
});
}
/**
* Returns all elements except for the set specified.
* @param items
*/
Except(items) {
return this.mutableArrayQuery((array) => {
const stringifiedItemsToExclude = JSON.stringify(items);
array = array.filter(item => !stringifiedItemsToExclude.includes(JSON.stringify(item)));
return Queryable.From(array);
});
}
/**
* Returns a random item from the array based on Knuth shuffle
* @param excluding - optionally exclude an array of items when selecting a random element
*/
GetRandom(excluding) {
const candidateElements = excluding ? this.Except(excluding) : this;
const shuffledItems = candidateElements.Shuffle();
return shuffledItems.length >= 1 ? shuffledItems[0] : null;
}
/**
* Sorts the elements of a sequence in ascending order based on the default comparer or user defined function(s)
* @param funcs
*/
OrderBy(funcs) {
return this.mutableArrayQuery((array) => {
if (!funcs) {
array.sort();
}
else {
array.sort((a, b) => {
let result = 0;
for (let index = 0; index < funcs.length; index++) {
const func = funcs[index];
if (func(a) < func(b)) {
result = -1;
break;
}
else if (func(a) > func(b)) {
result = 1;
break;
}
}
return result;
});
}
return Queryable.From(array);
});
}
/**
* Sorts the elements of a sequence in descending order.
* @param func
*/
OrderByDescending(funcs) {
return Queryable.From(this.OrderBy(funcs).reverse());
}
/**
* Returns the first element of a sequence or null if the sequence is empty.
* @param func optionally retrieve the first element which satisfies the given predicate
*/
First(func) {
if (func) {
return this.find(func) || null;
}
else {
const firstElement = this.length >= 1 ? this[0] : null;
return firstElement;
}
}
/**
* Returns the last element of a sequence or null if the sequence is empty.
* @param func optionally retrieve the last element which satisfies the given predicate
*/
Last(func) {
if (func) {
return this.slice().reverse().find(func) || null;
}
else {
const lastElement = this.length >= 1 ? this[this.length - 1] : null;
return lastElement;
}
}
/**
* Returns the element with the minimum value in a sequence of values.
* @param func
*/
Min(func = item => item) {
return this.length < 1 ?
null :
this.reduce((current, next) => func(current) < func(next) ? current : next, this[0]);
}
/**
* Returns the element with the maximum value in a sequence of values.
* @param func
*/
Max(func = item => item) {
return this.length < 1 ?
null :
this.reduce((current, next) => func(current) > func(next) ? current : next, this[0]);
}
/**
* Computes the sum of a sequence of numeric values, or the sum result of the given function
* @param func
*/
Sum(func) {
try {
let sum = 0;
if (func) {
this.forEach(element => {
sum += func(element);
});
}
else {
this.forEach(element => {
const tNumber = parseFloat(element.toString());
if (isNaN(tNumber)) {
throw new Error(`Sum failed - \"${tNumber}\" is not a number.`);
}
sum += tNumber;
});
}
return sum;
}
catch (error) {
module_1.Logger.Instance.Log(new module_1.LogEntry(error.message, module_1.LogLevel.Error, error));
return null;
}
}
/**
* Computes the average of a sequence of numeric values, or the average result of the given function
* @param func
*/
Average(func) {
const sum = func ? this.Sum(func) : this.Sum();
return this.length > 0 && sum ?
sum / this.length :
null;
}
/**
* Returns distinct elements from a sequence.
* @param func
*/
Distinct() {
const itemsToReturn = [];
for (let index = 0; index < this.length; index++) {
const element = this[index];
const stringifiedItems = JSON.stringify(itemsToReturn);
if (!stringifiedItems.includes(JSON.stringify(element))) {
itemsToReturn.push(element);
}
}
return Queryable.From(itemsToReturn);
}
/**
* V8 change - change params to an object
* Perform a full text search on a collection for a given search term. Elements containing the entire search term
* are given precedence over keyword matches.
* @param term The term being searched for
* @param minimumKeywordLength Keywords in the search term with a length less than this won't be considered
* @param stopWords Keywords matching these words are not considered
* @param ignorableSuffixCharacters Characters that should not prevent a positive match
* (i.e. allows toy's' to match on toy)
*/
Search(term, minimumKeywordLength = 3, stopWords = new Array(), ignorableSuffixCharacters = new Array(), fuzzyMatchPercentage = 0) {
const keywords = this.getKeywordsForTerm(term, ignorableSuffixCharacters);
stopWords = stopWords.map(s => s.toLowerCase());
const exactMatches = this.filter(item => JSON.stringify(item).toLowerCase().includes(term.toLowerCase())).slice();
const keywordMatches = this.getKeywordMatches(keywords, minimumKeywordLength, stopWords, fuzzyMatchPercentage);
const distinctResults = Queryable.From(exactMatches.concat(keywordMatches)).Distinct();
return distinctResults;
}
getKeywordsForTerm(term, ignorableSuffixCharacters) {
const keywords = term.split(Strings_1.Strings.Space);
keywords.forEach(element => {
element = element.replace(Regex_1.Regex.NonAlphaNumeric, Strings_1.Strings.Empty);
const lastCharacter = element[element.length - 1];
if (ignorableSuffixCharacters && ignorableSuffixCharacters.includes(lastCharacter)) {
keywords.push(element.split(lastCharacter)[0]);
}
});
return keywords;
}
getKeywordMatches(keywords, minimumKeywordLength, stopWords, fuzzyMatchPercentage) {
let keywordMatches = new Array();
if (keywords.length > 0) {
keywords.forEach(keyword => {
if (keyword.length >= minimumKeywordLength && !stopWords.includes(keyword.toLowerCase())) {
const keywordMatchesFound = this.filter(item => {
const stringifiedItem = JSON.stringify(item).toLowerCase();
let matches = stringifiedItem.includes(keyword.toLowerCase());
if (fuzzyMatchPercentage > 0) {
const itemKeywords = stringifiedItem.split(/[\s\"]/);
itemKeywords.forEach(itemKeyword => {
const increment = 100 / itemKeyword.length;
let matchPercentage = 0;
itemKeyword.split('').forEach((l, i) => {
var _a;
if (l.toLowerCase() === ((_a = keyword[i]) === null || _a === void 0 ? void 0 : _a.toLowerCase())) {
matchPercentage += increment;
}
});
if (matchPercentage >= fuzzyMatchPercentage) {
matches = true;
}
});
}
return matches;
});
keywordMatches = keywordMatches.concat(keywordMatchesFound.slice());
}
});
}
return keywordMatches;
}
mutableArrayQuery(func) {
return Queryable.From(func(this.slice()));
}
}
exports.Queryable = Queryable;
//# sourceMappingURL=Queryable.js.map