Home Manual Reference Source Test

src/filters/filterNumberProperty.js

/**
 * Convert the param to valid expression object for filter function
 * @param {number|numberSearchSyntax} param The param to be converted
 * @return {Object} If valid, returns a object. If not, returns undefined
 * @property {string} operator The operator for matching process
 * @property {number} number  The extracted number for matching process
 * @example
 * // returns { operator: '==' , number: 5 }
 * convertToValidExpression(5);
 * @example
 * // returns { operator: '>=' , number: 5 }
 * convertToValidExpression(">5");
 * @example
 * // returns undefined
 * convertToValidExpression(undefined);
 */
export function convertToValidExpression(param) {
  const validExpression = /^(==|>|<|>=|<=)(\d+)$/;
  let returnValue;
  // if it is a valid number expression like the regex
  if (validExpression.test(param)) {
    let result = param.match(validExpression);
    returnValue = {
      operator: result[1],
      number: Number(result[2]),
    };
  }
  // if the param is a number
  if (Number.isInteger(param)) {
    returnValue = {
      operator: '==',
      number: param,
    };
  }
  return returnValue;
}

/**
 * Filter function for filterByNumber
 * @param {string} property The property to be checked
 * @param {Object} expressionObject The object from convertToValidExpression
 * @param {string} expressionObject.operator The operator for matching process
 * @param {number} expressionObject.number  The extracted number for matching process
 * @param {TPN} object the object to be checked
 * @return {boolean} the result
 */
function resolveExpression(property, expressionObject, object) {
  let { operator, number } = expressionObject;
  // No : eval is not all evil but you should know what you are doing
  // eslint-disable-next-line no-eval
  return eval(`${object[property]}${operator}${number}`);
}

/**
 * Provides a map with valid default properties
 * @param {searchParameters} searchObject - search parameters
 * @return {Map<string, numberExpressionObject>} the result map
 */
export function filterDefaultNumberProperties(searchObject) {
  const {
    season, episode, year,
  } = searchObject;


  const propertiesArray = [season, episode, year];
  const propertiesNames = ['season', 'episode', 'year'];

  return propertiesArray.reduce((propertiesMap, val, index) => {
    if (val !== undefined) {
      propertiesMap.set(propertiesNames[index], convertToValidExpression(val));
    }
    return propertiesMap;
  }, new Map());
}

/**
 * Remove the default number properties
 * @param {searchParameters} searchObject - search parameters
 * @return {searchParameters} searchParameters without these properties
 */
export function excludeDefaultNumberProperties(searchObject) {
  const {
    season, episode, year,
    ...rest
  } = searchObject;
  return rest;
}

/**
 * Filter the set based on string properties
 * @param {Set<TPN>} set The TPN set
 * @param {Map<string, numberExpressionObject>} propertiesMap The map from filterDefaultStringProperties
 * @return {Set<TPN>} the filtered set
 */
export function filterByNumber(set, propertiesMap) {
  // first step : get an array so that we can do filter/reduce stuff
  // second step : iterate the propertiesMap and do filter and return the filtered array
  // val[0] : the key ; val[1] : the value
  return new Set(Array
    .from(propertiesMap.entries())
    .reduce(
      // eslint-disable-next-line max-len
      (currentMoviesArray, val) => currentMoviesArray.filter(TPN => resolveExpression(val[0], val[1], TPN))
      , [...set],
    ));
}