Рефакторинг функций динамического фильтра

Я создаю этот компонент фильтра меню:

Меню

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

чтобы сгенерировать такой фильтр, я фильтрую по отфильтрованной опции и так далее, другими словами, так:

return props.tasks.filter(x => {
    if (props.key.includes('task-status')) {
      return props.status.some(status => x['task-status'] === status)
    }
    return true
  }).filter(x => {
    if (props.key.includes('task-priority')) {
      return props.priority.reduce((vres, val) => vres.concat(val), []).some(priority => x['task-priority'] === priority)
    }
    return true;
  }).filter(x => {
    if (props.key.includes('task-actual-owner')) {
      return props.users.some(user => x['task-actual-owner'] === user)
    }
    return true;
  }).filter(x => {
    if (props.key.includes('task-expiration-time')) {
      if (x['task-expiration-time'] === null) {
        return false;
      }

      const f = props.expiration.flat();
      return x['task-expiration-time']['java.util.Date'] >= f[0] && x['task-expiration-time']['java.util.Date'] <= f[1]
    }
    return true;
  })

Итак, весь список карточек происходит из этого массива объектов:

array = [
  {
    "task-id": 142,
    "task-name": "Task",
    "task-subject": "",
    "task-description": "",
    "task-status": "Ready",
    "task-priority": 0,
    "task-is-skipable": false,
    "task-actual-owner": null,
    "task-created-by": null,
    "task-created-on": {
      "java.util.Date": 1606322625000
    },
    "task-activation-time": {
      "java.util.Date": 1606322625000
    },
    "task-expiration-time": {
      "java.util.Date": 1200832320000
    },
    "task-proc-inst-id": 187,
    "task-proc-def-id": "businessProcess.main",
    "task-container-id": "businessProcess_1.0.1-SNAPSHOT",
    "task-parent-id": -1,
    "correlation-key": "187",
    "process-type": 1
  },
  {
    "task-id": 141,
    "task-name": "Task",
    "task-subject": "",
    "task-description": "",
    "task-status": "InProgress",
    "task-priority": 9,
    "task-is-skipable": false,
    "task-actual-owner": "john.doe",
    "task-created-by": null,
    "task-created-on": {
      "java.util.Date": 1606322577000
    },
    "task-activation-time": {
      "java.util.Date": 1606322577000
    },
    "task-expiration-time": {
      "java.util.Date": 1674200580000
    },
    "task-proc-inst-id": 186,
    "task-proc-def-id": "businessProcess.main",
    "task-container-id": "businessProcess_1.0.1-SNAPSHOT",
    "task-parent-id": -1,
    "correlation-key": "186",
    "process-type": 1
  },
  {
    "task-id": 140,
    "task-name": "Task",
    "task-subject": "",
    "task-description": "",
    "task-status": "Reserved",
    "task-priority": 6,
    "task-is-skipable": false,
    "task-actual-owner": "peter.griffin",
    "task-created-by": null,
    "task-created-on": {
      "java.util.Date": 1606322524000
    },
    "task-activation-time": {
      "java.util.Date": 1606322524000
    },
    "task-expiration-time": {
      "java.util.Date": 1863598080000
    },
    "task-proc-inst-id": 185,
    "task-proc-def-id": "businessProcess.main",
    "task-container-id": "businessProcess_1.0.1-SNAPSHOT",
    "task-parent-id": -1,
    "correlation-key": "185",
    "process-type": 1
  },
  {
    "task-id": 139,
    "task-name": "Task",
    "task-subject": "",
    "task-description": "",
    "task-status": "Reserved",
    "task-priority": 0,
    "task-is-skipable": false,
    "task-actual-owner": "homer.simpson",
    "task-created-by": null,
    "task-created-on": {
      "java.util.Date": 1606322446000
    },
    "task-activation-time": {
      "java.util.Date": 1606322446000
    },
    "task-expiration-time": null,
    "task-proc-inst-id": 184,
    "task-proc-def-id": "businessProcess.main",
    "task-container-id": "businessProcess_1.0.1-SNAPSHOT",
    "task-parent-id": -1,
    "correlation-key": "184",
    "process-type": 1
  },
  {
    "task-id": 138,
    "task-name": "Task",
    "task-subject": "",
    "task-description": "",
    "task-status": "Reserved",
    "task-priority": 0,
    "task-is-skipable": false,
    "task-actual-owner": "jim.carrey",
    "task-created-by": null,
    "task-created-on": {
      "java.util.Date": 1606322412000
    },
    "task-activation-time": {
      "java.util.Date": 1606322412000
    },
    "task-expiration-time": null,
    "task-proc-inst-id": 183,
    "task-proc-def-id": "businessProcess.main",
    "task-container-id": "businessProcess_1.0.1-SNAPSHOT",
    "task-parent-id": -1,
    "correlation-key": "183",
    "process-type": 1
  }
]

и массив объектов, созданный с помощью нескольких вариантов выбора, может быть таким (например):

[
  {
    key: "task-status",
    label: "Waiting For",
    status: "Ready"
  },
  {
    key: "task-status",
    label: "Picked Up",
    status: "Reserved"
  }
  {
    key: "task-priority",
    label: "Low",
    priority: [0, 1, 2, 3, 4]
  },
  {
    key: "task-actual-owner",
    label: "John Doe",
    owner: "john.doe"
  },
  {
    key: "task-expiration-time",
    expiration: [1611598726000, 1611609526000]
  }
];

Все работает правильно, но я пытаюсь найти способ рефакторинга первой функции … (return props.tasks.filter(x => { ...) Есть ли способ добиться этого?

1 ответ
1

Есть две основные вещи, которые вы можете сделать, чтобы немного очистить его:

  1. Вместо того, чтобы связывать кучу .filter()s вместе, создайте список функций фильтра, которые вы хотите использовать, а затем используйте только выбранные.
  2. Вы можете немного разбить функцию, вытащив и назвав каждую из функций фильтра. Это может упростить понимание с первого взгляда, какую логику выполняет ваша функция.

Вот один из способов закодировать его после применения этих двух идей. (Следующее не проверено, но должно дать представление)

function applyFilters(props) {
  const filters = [];
  if (props.key.includes('task-status')) {
    filters.push(createFilterFor.status(props));
  }
  if (props.key.includes('task-priority')) {
    filters.push(createFilterFor.priority(props));
  }
  if (props.key.includes('task-actual-owner')) {
    filters.push(createFilterFor.owner(props));
  }
  if (props.key.includes('task-expiration-time')) {
    filters.push(createFilterFor.expirationTime(props));
  }

  return props.tasks.filter(combineFilters(filters));
}

const combineFilters = filters => (
  entry => filters.every(filter => filter(entry))
);

const createFilterFor = {
  status: ({ status }) => (
    task => status.some(statusText => task['task-status'] === statusText)
  ),
  priority: ({ priority }) => (
    task => priority.flat(1).some(priorityText => task['task-priority'] === priorityText)
  ),
  owner: ({ users }) => (
    task => users.some(user => task['task-actual-owner'] === user)
  ),
  expirationTime: ({ expiration }) => (
    task => {
      if (!task['task-expiration-time']) return false;

      const taskExpiration = task['task-expiration-time']['java.util.Date'];
      const [start, end] = expiration.flat();
      return taskExpiration >= start && taskExpiration <= end;
    }
  ),
};

Если вы обнаружите, что добавляете множество функций фильтра, которые следуют этому шаблону, вы можете добавить уровень абстракции, который позволит вам удалить цепочку if-then, но это сделает код более нечитаемым и менее гибким.

function applyFilters(props) {
  const filters = Object.entries(createFilterFor)
    .filter(([key]) => props.keys.includes(key))
    .map(([, createFilter]) => createFilter(props));

  return props.tasks.filter(combineFilters(filters));
}

const combineFilters = filters => (
  entry => filters.every(filter => filter(entry))
);

const createFilterFor = {
  'task-status': ({ status }) => (
    task => status.some(statusText => task['task-status'] === statusText)
  ),
  'task-priority': ({ priority }) => (
    task => priority.flat(1).some(priorityText => task['task-priority'] === priorityText)
  ),
  'task-actual-owner': ({ users }) => (
    task => users.some(user => task['task-actual-owner'] === user)
  ),
  'task-expiration-time': ({ expiration }) => (
    task => {
      if (!task['task-expiration-time']) return false;

      const taskExpiration = task['task-expiration-time']['java.util.Date'];
      const [start, end] = expiration.flat();
      return taskExpiration >= start && taskExpiration <= end;
    }
  ),
};

Обновить

Судя по отзывам @ DiddyO, мне кажется, я лучше понимаю, какова цель этого вопроса. Вот обновленная, гораздо более упрощенная версия, которая должна делать то, что просили (при условии, что я правильно понял комментарий).

(Еще раз, это непроверено и может содержать пару ошибок, но должно дать представление о том, как выполнить эту задачу)

const applyFilters = props => (
  props.tasks.filter(createFilterFromProps(props))
);

const createFilterFromProps = props => (
  entry => Object.entries(filters).every(
    ([key, filter]) => !props.key.includes(key) || filter(props, entry[key])
  )
);

const filters = {
  'task-status': ({ status }, taskStatus) => status.some(statusText => taskStatus === statusText),
  'task-priority': ({ priority }, taskPriority) => priority.flat(1).some(priorityText => taskPriority === priorityText),
  'task-actual-owner': ({ users }, actualOwner) => users.some(user => user === actualOwner),
  'task-expiration-time': ({ expiration }, taskExpiration) => {
    if (!taskExpiration) return false;
    const [start, end] = expiration.flat();
    return taskExpiration['java.util.Date'] >= start && taskExpiration['java.util.Date'] <= end;
  },
};
```

  • Большое спасибо … действительно полезно, я подумал о другом, но я не уверен, я подумал о создании функции, которая не требует нескольких проходов фильтра. Что-то, что может уменьшить массив объектов фильтра до логического значения и использовать его в одном фильтре, просто объединяя пользователей, приоритет, статус и так далее, и используя ключ из объекта obj. Я не хочу использовать жестко запрограммированные строки, такие как «статус задачи», потому что я уже определил ключ в объекте фильтра.

    – Дидди О.

  • @DiddyO. Я обновил ответ новым фрагментом кода, который, надеюсь, должен делать то, что вы хотите.

    – Скотти Джеймисон

  • большое спасибо за твою помощь. Действительно полезно

    – Дидди О.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *