UNPKG

tsbase

Version:

Base class libraries for TypeScript

248 lines 9.92 kB
"use strict"; 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