lodash - функция фильтра, использующая массив объектов фильтров - PullRequest
0 голосов
/ 03 июля 2018

Интересно, как я мог бы использовать lodash для фильтрации некоторых объектов, используя массив параметров фильтров.

Пока у меня есть функция, которая может использовать опцию массива фильтров, но мне не удается учесть оператор LOGICAL (AND, OR).

Edit:

мы находим те объекты, которые соответствуют каждому условию LOGICAL: AND или любому условию LOGICAL: OR

См. Эту демонстрацию:

var toFilter = [
    {"foo" : 1, "bar": 0}, 
    {"foo" : 1, "bar": 0}, 
    {"foo" : 2, "bar": null}
];

var filters = [
    {"KEY": "foo", "VALUE": 1, "EQUALITY": "EQUAL", "LOGICAL": "OR"}, 
    {"KEY": "bar", "VALUE": 0, "EQUALITY": "NOT_EQUAL", "LOGICAL": "AND"}
];

//the function should be equivalent to this one:
_.filter(toFilter, function(obj) { return obj.foo == 1 || obj.bar != 0 });

//this function does not take the LOGICAL operator ("AND" and "OR") in consideration
_.uniq(_.flatMap(filters, function(filter) {
     return _.filter(toFilter, function(obj) {
        if (filter.EQUALITY == "EQUAL") {
            return obj[filter.KEY] == filter.VALUE;
        } else {
            return obj[filter.KEY] != filter.VALUE;
        }
     });
}));

Это очень простой пример, но список фильтров может быть намного больше.

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Это будет мой подход к решению проблемы:

var toFilter = [{ foo: 1, bar: 0 }, { foo: 1, bar: 0 }, { foo: 2, bar: null }];

var filters = [
  { KEY: 'foo', VALUE: 1, EQUALITY: 'EQUAL', LOGICAL: 'OR' },
  { KEY: 'bar', VALUE: 0, EQUALITY: 'NOT_EQUAL', LOGICAL: 'AND' }
];

var orFilters = _.filter(filters, ['LOGICAL', 'OR']);
var andFilters = _.filter(filters, ['LOGICAL', 'AND']);

var result = _.filter(toFilter, evalFilters);

function evalFilters(obj) {
  var curriedEquality = equality(obj);
  return (
    _.some(orFilters, curriedEquality) ||
    (andFilters.length && _.every(andFilters, curriedEquality))
  );
}

function equality(obj) {
  return function(filter) {
    return filter.EQUALITY === 'EQUAL'
      ? obj[filter.KEY] === filter.VALUE
      : obj[filter.KEY] !== filter.VALUE;
  };
}

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Первым шагом является разделение фильтров OR и фильтров AND на два разных массива.

Затем я перебрал бы элементы, которые нужно отфильтровать, сделал бы карри-функцию equality, которая получит оцениваемый элемент и вернет функцию, содержащую сравнение на равенство.

Тогда я бы использовал _.some, чтобы проверить, имеет ли элемент значение true, по крайней мере, в одном фильтре OR, если нет, я бы использовал _.every, чтобы проверить, имеет ли элемент значение true во всех фильтрах AND. .

Obs: Необходимо проверить, не является ли массив andFilters пустым, потому что _.every, а также Array.prototype.every вернет true, если это так.

Надеюсь, это объяснение поможет!

0 голосов
/ 03 июля 2018

Я думаю, что вы ищете Array.prototype.every и Array.prototype.some. Это может сделать то, что вам нужно. Если есть реальные дубликаты, которые вам нужно удалить, то lodash uniq может быть добавлен достаточно легко.

const conditions = {
  'EQUAL': (a, b) => a === b,
  'NOT_EQUAL': (a, b) => a !== b,
}

const check = (objs, filters) => objs.filter(obj => {
  const ands = filters.filter(filter => filter.LOGICAL === 'AND')
  const ors = filters.filter(filter => filter.LOGICAL === 'OR')
  return ors.some(
    filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)  
  ) || (ands.length && ands.every(
    filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)  
  ))
})


const toFilter = [
  {foo : 1, bar: 0,    id: 'a'}, 
  {foo : 1, bar: 0,    id: 'b'}, 
  {foo : 2, bar: null, id: 'c'},
  {foo : 2, bar: 0,    id: 'd'}, 
];

const filters = [
  {KEY: "foo", VALUE: 1, EQUALITY: "EQUAL",     LOGICAL: "OR"}, 
  {KEY: "bar", VALUE: 0, EQUALITY: "NOT_EQUAL", LOGICAL: "AND"}
];

console.log(check(toFilter, filters))  //=> ids a, b and c

Обратите внимание, что этот дизайн позволяет легко добавлять новые условия. Например:

  'GREATER_THAN': (a, b) => a > b

Если это для сети, и у вас нет процесса сборки, я бы порекомендовал сохранить условия в локальном замыкании для функции с чем-то вроде этого (не проверено):

const check = (() => {
  const conditions = {
    'EQUAL': (a, b) => a === b,
    'NOT_EQUAL': (a, b) => a !== b,
  }

  return (objs, filters) => (objs, filters) => objs.filter(obj => {
    const ands = filters.filter(filter => filter.LOGICAL === 'AND')
    const ors = filters.filter(filter => filter.LOGICAL === 'OR')
    return ors.some(
      filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)  
    ) || (ands.length && ands.every(
      filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)  
    ))
  })
})()
...