UNPKG

generate-words-freqmap

Version:
124 lines (114 loc) 4.78 kB
'use strict'; class CustomError extends Error { /** * Create a `CustomError` (same as `Error` but with an extra property called `library`). * @param message Argument to be passed to base constructor (`Error()`). * @param library Name of the library in which error will be thrown. */ constructor(message, library) { super(message); this.library = library; } toString() { return `Error in \`${this.library}\` library:\n\t${this.message}`; } } // messages aren't inside an object because // tree-shaking isn't possible when exporting an object // https://medium.com/@rauschma/note-that-default-exporting-objects-is-usually-an-anti-pattern-if-you-want-to-export-the-cf674423ac38#.nibatprx3 // used at `fullfiller/src/validate` const notEnoughWordsInFreqMap = (minimum, received) => `Given \`wordsArray\` doesn't have enough words to construct \`freqMap\` containing the minimum quantity of words required. Minimum number of words required: ${minimum}. Number of words received: ${received}.`; /** * Every freqMap's word in `wordsToEmphasize` will have their weight multiplied by `emphasizeBy`. */ function emphasize(freqMapWordAsKey, wordsToEmphasize, emphasizeBy) { return wordsToEmphasize.reduce((freqMap, word) => freqMap[word] === undefined ? freqMap : { ...freqMap, [word]: Math.round(freqMap[word] * emphasizeBy) }, freqMapWordAsKey); } /** **freqMapWeightAsKey** example: `{ 1: ['foo', 'bar'], 3: ['baz'] }` */ function generateFreqMapWeightAsKey(freqMapWordAsKey) { return Object.keys(freqMapWordAsKey).reduce((freqMap, word) => { const weight = freqMapWordAsKey[word]; return { ...freqMap, [weight]: (freqMap[weight] || []).concat(word) }; }, {}); } /** **freqMapWordAsKey** example: `{ foo: 1, bar: 3 }` */ function generateFreqMapWordAsKey(wordsArray, caseInsensitive) { return wordsArray.reduce((freqMap, word) => { const w = caseInsensitive ? word.toLowerCase() : word; return { ...freqMap, // using hasOwnProperty instead of check if undefined, otherwise // if `word` is a built-in object property (e.g. `constructor`) // would append 1 to this property (which would results in `NaN`) // instead of creating a new property [w]: Object.prototype.hasOwnProperty.call(freqMap, w) ? freqMap[w] + 1 : 1 }; }, {}); } function getFreqMapWordsQuantity(freqMap) { const wordsQuantity = Object.values(freqMap).reduce((acc, cur) => acc + cur.length, 0); return wordsQuantity; } /** If required by `options`, reduce the quantity of tiers in `freqMap`. */ function shortenFreqMap(freqMap, tierWeightMin, tierWeightMax, mergePosteriorTiersAt) { return Object.entries(freqMap).reduce((shortened, entry) => { const weight = Number(entry[0]); const tier = entry[1]; // if current weight is less than min or more than max, filter out current tier if (weight < tierWeightMin || tierWeightMax !== -1 && // -1 would indicate that `tierWeightMax` functionality is disabled weight > tierWeightMax) { return shortened; } // if current weight is more than mergePosteriorTiersAt // merge current tier into mergePosteriorTiersAt tier if (mergePosteriorTiersAt !== -1 && // -1 would indicate that `mergePosteriorTiersAt` functionality is disabled weight > mergePosteriorTiersAt) { return { ...shortened, [mergePosteriorTiersAt]: (shortened[mergePosteriorTiersAt] || []).concat(freqMap[weight]) }; } return { ...shortened, [weight]: tier }; }, {}); } const optionsDefault = { emphasizeBy: 2, wordsQuantityMin: 0, tierWeightMin: 1, tierWeightMax: -1, mergePosteriorTiersAt: -1, caseInsensitive: false }; /** * Generate `freqMap` from `wordsArray`. * @param wordsArray * @param wordsToEmphasize Subset of `wordsArray` to emphasize. * @param optionsArg Miscellaneous options. * @throws Error if `freqMap` has less words than required by `options.wordsQuantityMin`. * @returns freqMap. */ function generateFreqMap(wordsArray, wordsToEmphasize, optionsArg) { const options = { ...optionsDefault, ...optionsArg }; const freqMapWordAsKey = emphasize(generateFreqMapWordAsKey(wordsArray, options.caseInsensitive), wordsToEmphasize || [], options.emphasizeBy); const freqMap = shortenFreqMap(generateFreqMapWeightAsKey(freqMapWordAsKey), options.tierWeightMin, options.tierWeightMax, options.mergePosteriorTiersAt); const freqMapWordsQuantity = getFreqMapWordsQuantity(freqMap); if (freqMapWordsQuantity < options.wordsQuantityMin) { throw new CustomError(notEnoughWordsInFreqMap(options.wordsQuantityMin, freqMapWordsQuantity), 'generate-words-freqmap'); } return freqMap; } module.exports = generateFreqMap;