Skip to content

Instantly share code, notes, and snippets.

@dmitry-tuzenkov
Last active August 8, 2022 11:21
Show Gist options
  • Save dmitry-tuzenkov/341cf0ef3945b22476d5485a1fc86e14 to your computer and use it in GitHub Desktop.
Save dmitry-tuzenkov/341cf0ef3945b22476d5485a1fc86e14 to your computer and use it in GitHub Desktop.
Filters Implementation
const _get = require('lodash.get')
const _isEmpty = require('lodash.isempty')
const _sumBy = require('lodash.sumby')
const _max = require('lodash.max')
const _flatten = require('lodash.flatten')
const _last = require('lodash.last')
const _size = require('lodash.size')
const _first = require('lodash.first')
const _map = (arr = [], fn) => Array.from(arr).map(fn)
const _filter = (arr = [], fn) => Array.from(arr).filter(fn)
const _omit = (obj = {}, arr = []) =>
Object.entries(obj).reduce((acc, x) => {
const [key, value] = x
if (arr.includes(key)) {
return acc
}
return { ...acc, [key]: value }
}, {})
module.exports = {
_first,
_filter,
_flatten,
_get,
_map,
_omit,
_isEmpty,
_sumBy,
_max,
_size,
_last,
}
const FILTER_CONDITION = { OR: '+', AND: '*' }
const FILTER_TYPE = { ROWS: 'r', COLUMNS: 'c', GROUP: 'g' }
const FILTER_ACTIONS = {
CONTAIN: 'cn',
NOT_CONTAIN: '!cn',
EQUAL: 'eq',
NOT_EQUAL: '!eq',
EMPTY: 'emp',
NOT_EMPTY: '!emp',
}
module.exports = {
FILTER_ACTIONS,
FILTER_CONDITION,
FILTER_TYPE,
IHUB_API_URL,
PRODUCT,
USERAGENT,
}
const filters = [
{
id: _last(workflow).id,
type: FILTER_TYPE.ROWS,
expr: [
[FILTER_CONDITION.AND, FILTER_ACTIONS.NOT_EMPTY, 'twitterTweet'],
[
FILTER_CONDITION.OR,
FILTER_ACTIONS.CONTAIN,
'websiteUrl',
URL_CONSTANT,
],
],
},
{
id: _last(workflow).id,
type: FILTER_TYPE.ROWS,
expr: [
[
FILTER_CONDITION.AND,
FILTER_ACTIONS.CONTAIN,
'twitterTweet',
TWEET_CONSTANT,
],
],
},
{
id: _last(workflow).id,
type: FILTER_TYPE.COLUMNS,
expr: [[FILTER_CONDITION.AND, FILTER_ACTIONS.NOT_EMPTY, 'tags']],
},
]
/* eslint-disable indent */
const {
FILTER_ACTIONS,
FILTER_CONDITION,
FILTER_TYPE,
} = require('./constants')
const {
_size,
_filter,
_map,
_omit,
_last,
_first,
_isEmpty,
} = require('./common')
// Expressions example array:
// [FILTER_CONDITION.OR, 'twitterTweet', FILTER_ACTIONS.NOT_EMPTY],
// [
// FILTER_CONDITION.OR,
// 'websiteUrl',
// FILTER_ACTIONS.CONTAIN,
// URL_CONSTANT,
// ],
const isConst = (key = '') => {
const arr = Array.from(key)
return _first(arr) === '"' && _last(arr) === '"'
}
/**
* Extract a value from data object
* @param {Object} data
* @param {string} key
* @returns {string}
*/
const toValue = (data = {}, key = '') => {
if (!isConst(key)) {
return data[key]
}
// if value or constant
const arr = Array.from(key)
arr.shift()
arr.pop()
return arr.join('')
}
/**
* Expressions cmparator
* @param {string} op operator
* @returns {function}
*/
const compareExpression =
(op) =>
/**
* Compare expression
*
* @param {string} aVal
* @param {string} bVal
* @returns {Boolean}
*/
(aVal = '', bVal = '') => {
switch (op) {
case FILTER_ACTIONS.CONTAIN:
return String(aVal).includes(String(bVal))
case FILTER_ACTIONS.NOT_CONTAIN:
return !String(aVal).includes(String(bVal))
case FILTER_ACTIONS.EMPTY:
return _isEmpty(String(aVal))
case FILTER_ACTIONS.NOT_EMPTY:
return !_isEmpty(String(aVal))
default:
return false
}
}
/**
* Filters expressions comparator
*
* @param {Object} data
* @param {Array} expr
* @returns {Boolean}
*/
const compareExpressions = (data = {}, expr = []) => {
return expr.reduce((acc, x) => {
const [fCond, fOp, aKey, bKey] = x
const toCompare = compareExpression(fOp)
const compared = toCompare(toValue(data, aKey), toValue(data, bKey))
if (fCond === FILTER_CONDITION.AND) {
return acc && compared
}
return acc || compared
}, true)
}
const isConditionTrue = (data = {}, cond = {}) =>
compareExpressions(data, cond.expr)
/**
* Extract condition keys filter is [FILTER_CONDITION.AND, FILTER_ACTIONS.NOT_EMPTY, 'tags']
*
* @param {Object} cond
* @returns {Array} of keys
*
*/
const getCondKeys = ({ expr = [] }) => _map(Array.from(expr), (x) => x[2])
/**
* Handles a piece of data
*
* @param {Array} arrContext of collection of objects
* @param {Object} match a filter object
*/
const handle = (arrContext = [], cond = {}) => {
let _arrContext = []
switch (cond.type) {
case FILTER_TYPE.ROWS: {
_arrContext = _filter(arrContext, (x) => isConditionTrue(x, cond))
break
}
case FILTER_TYPE.COLUMNS: {
// Expiremental
_arrContext = _map(arrContext, (x) => {
return isConditionTrue(x, cond) ? _omit(x, getCondKeys(cond)) : x
})
break
}
// TODO: TBD
case FILTER_TYPE.GROUP:
default: {
_arrContext = [...arrContext]
break
}
}
return _arrContext
}
/**
* Filter content arrays
*
* @param {Object} action action object
* @param {Array} conditions of filters collection of objects
* @param {Array} arrContext of collection of objects
* @returns {Array} collection of filtrated objects by filters
*/
const applyFilters = (action = {}, conditions = [], arrContext = []) => {
const _conditions = _filter(conditions, (x) => x.id === action.id)
if (_size(_conditions)) {
return _conditions.reduce(handle, arrContext)
}
return arrContext
}
module.exports = { applyFilters }
@Kontsedal
Copy link

N I B A G A N A

@dmitry-tuzenkov
Copy link
Author

K P A C U B O

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment