Как динамически построить выражение фильтра массива в javascript - PullRequest
0 голосов
/ 10 апреля 2020

Мне нужна функция для фильтрации моего массива и возврата результатов на основе пользовательских данных.

var Myarray= [
  {Date:"1-Jan-2020",Name:"A", Id: 1},
  {Date:"1-Feb-2020",Name:"B", Id: 2},
  {Date:"1-Mar-2020",Name:"C", Id: 3}
  ...
]

Фильтр должен быть динамическим c. Пользователь ДОЛЖЕН ввести как минимум дату или имя или оба вместе. Идентификатор необязательно

Существует ли разумный способ построения выражения фильтра в функции на основе того, что когда-либо вводит пользовательский ввод?

Дополнительная информация: У меня есть пользовательский интерфейс. У пользователя будут параметры поиска для выбора даты возвращения. Дата, Имя и ID. Пользователь ДОЛЖЕН выбрать хотя бы Дату или Имя. Идентификатор необязателен, но должен учитываться в функции, которая будет принимать отправленные параметры. После того, как пользователи вводят значения, они передаются в функцию JS, которая должна фильтровать массив, содержащий данные. После применения фильтров к массиву новый массив заполняется отфильтрованными данными. Ex.user отправляет Date без имени или или ID, затем массив фильтрует только по дате. Ex2. Пользователь отправляет Имя без даты или идентификатора, затем массив фильтрует только по имени. Пример 3. Пользователь отправляет дату и имя без идентификатора, затем фильтрует массив по дате и имени. Ex4. Пользователь отправляет дату и идентификатор без имени, затем фильтрует массив по дате и идентификатору. Ex5. Пользователь отправляет имя и идентификатор без даты, затем массив фильтрует по имени и идентификатору. Пользователь ex6 отправляет дату, имя и идентификатор, затем фильтрует массив по дате, имени и идентификатору. Пользователь Ex7 отправляет идентификатор, funtction НЕ разрешает отправку идентификатора без указания даты или имени

Ответы [ 3 ]

0 голосов
/ 10 апреля 2020

const Myarray= [
  {Date:"1-Jan-2020",Name:"A", Id: 1},
  {Date:"1-Feb-2020",Name:"B", Id: 2},
  {Date:"1-Mar-2020",Name:"C", Id: 3}
];

const customFilter = (array, expr) => {
  if (Object.keys(expr).length == 0) {
    console.log("Error! missing at least one filter expression");
    return null;
  }
  /* if expression doesn't exist it's a pass for all items */
  return array.filter(item => !expr.Date || item.Date == expr.Date).filter(item => !expr.Name || item.Name == expr.Name).filter(item => !expr.Id || item.Id == expr.Id)
}

console.log(customFilter(Myarray, {Date: "1-Jan-2020", Name: "B"}))
console.log(customFilter(Myarray, {Date: "1-Jan-2020"}))
console.log(customFilter(Myarray, {Name: "B"}))
console.log(customFilter(Myarray, {Id: 3}))
console.log(customFilter(Myarray, {}))
0 голосов
/ 10 апреля 2020

Вы можете использовать Array.prototype.some (ИЛИ) для цепочки предикатов. Если в какой-то момент предикат совпадает, весь элемент будет добавлен в итоговый список.

Если вы хотите, чтобы все условия соответствовали, вам нужно использовать Array.prototype.every (AND).

Я могу вернуться к этому, с более динамичным примером c, в ближайшее время. См. Ниже.

const main = () => {
  let arr = [
    { Date: "1-Jan-2020" , Name: "A"  , Id: 1 }, // Yes
    { Date: "1-Feb-2020" , Name: "B"  , Id: 2 }, // Yes
    { Date: "1-Mar-2020" , Name: null , Id: 3 }, // Yes
    { Date: null         , Name: null , Id: 4 }  // No
  ]
  
  console.log(filterWithPredicates(arr, predicates))
}

const predicates = {
  date: (record) => record.Date != null,
  name: (record) => record.Name != null
};

const filterWithPredicates = (list, predicates) => {
  return list.filter(item => {
    return Object.values(predicates).some(predicate => {
      if (predicate(item)) { return true }
    })
    return false
  })
}

main()
.as-console-wrapper { top: 0; max-height: 100% !important; }

Вот пример цепочки предикатов.

const Predicates = {
  date: (record) => record.Date != null,
  name: (record) => record.Name != null
}

const main = () => {
  let arr = [
    { Date: "1-Jan-2020" , Name: "A"  , Id: 1 }, // Yes
    { Date: "1-Feb-2020" , Name: "B"  , Id: 2 }, // Yes
    { Date: "1-Mar-2020" , Name: null , Id: 3 }, // Yes
    { Date: null         , Name: null , Id: 4 }  // No
  ]
  
  let filter = new Filter().chain().or(Predicates.date).or(Predicates.name)
  console.log(filter.execute(arr))
}

class Filter {
  constructor() {
    this.filters = []
  }
  chain() {
    this.filters = []
    return this
  }
  or(predicate) {
    this.filters.push({ fn : predicate, op : 'or' })
    return this
  }
  and(predicate) {
    this.filters.push({ fn : predicate, op : 'and' })
    return this
  }
  execute(items) {
    return items.reduce((results, item) => 
      this.__shouldKeepItem(item) ? results.concat(item) : results, [])
  }
  /** @private */
  __startCondition() {
    return this.filters.length ? this.filters[0].op === 'and' ? 1 : 0 : 0
  }
  /** @private */
  __shouldKeepItem(item) {
    return this.filters.reduce((keep, filter) => {
      switch (filter.op) {
        case 'or'  : return keep || filter.fn(item)
        case 'and' : return keep && filter.fn(item)
        default    : return keep
      }
    }, this.__startCondition())
  }
}

main()
.as-console-wrapper { top: 0; max-height: 100% !important; }
0 голосов
/ 10 апреля 2020

Попробуйте:

var filteredArray = Myarray.filter(function (item) {
   return item.Date !== undefined && item.Name !== undefined;
});
var filteredArrayWithOptionalId = Myarray.filter(function (item) {
   return item.Date !== undefined && item.Name !== undefined && item.Id !== undefined;
});

Теперь у вас есть 2 массива, один с необходимыми объектами параметров и один, который может обрабатывать необязательный идентификатор. Затем вы можете l oop через filteredArrayWithOptionalId обработать код, относящийся к идентификаторам.

См. Ответ на похожий вопрос здесь: Как отфильтровать массив объектов на основе атрибутов?

Также я рекомендую использовать имена переменных в нижнем регистре, так как это лучший метод, если они не являются классами.

...