Как я могу построить, в JavaScript цепочки фильтров, в стиле обертки лодашь это?


Чтобы было понятно, задан массив объектов, вероятно, с однородными ключами:

const data = [
  { name: "tomato", isHealthy: true, "calories": 300 },
  { name: "french fries", isHealthy: false, "calories": 1000 },
  { name: "lettuce", isHealthy: true, "calories": 100 },
  { name: "nacho cheese", isHealthy: false, "calories": 1200 },
  { name: "boring chicken", isHealthy: true, "calories": 500 },
];

И несколько фильтров говорят:

const isHealthy = function(items) {
  return items.filter(i => i.isHealthy);
};

const caloriesAbove = function(items, calories) {
  return items.filter(i => i.calories > calories);
};

Я хотел бы иметь возможность вызывать цепочку фильтров следующим образом:

wrapper(data).isHealthy().caloriesAbove(500).value.map(...)

Немного трудно понять, как лодаш добивается этого. Далее, Есть ли способ сделать это без необходимости явно разворачивать, чтобы получить значение? Это не требование, поскольку я думаю, что это может быть связано с использованием нежелательных хаков.
2 4

2 ответа:

Вот как это делает лодаш:

 function wrapper(items) {
    return {
        value: items,
        isHealthy() { 
          return wrapper(items.filter(it => it.isHealthy));
        },
        caloriesAbove(calories) {
          return wrapper(items.filter(it => it.calories > calories));
        },
    };
 }

Далее, есть ли способ сделать это без необходимости явно разворачивать, чтобы получить значение?

Начиная с ES6 можно расширять массивы:

  class Items extends Array {
    isHealthy() {
      return this.filter(it => it.isHealthy);
    }
  }

Чтобы превратить существующий массив в элементы, просто сделайте Items.from(array) или new Items(...array), тогда вы можете:

  items
    .filter(it => it.calories > 2) // native methods can be used, they return Item instances, not Arrays
    .isHealthy() // our custom methods also work
    .map(it => it.name)[0] //its a regular array with indices.

Я прочитал ответ, и мне понравилось, как это было сделано @Jonas. Не знал, что можно просто так расширить массив. Защита. получил мой апвот!

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

const data = [
  { name: "tomato", isHealthy: true, "calories": 300 },
  { name: "french fries", isHealthy: false, "calories": 1000 },
  { name: "lettuce", isHealthy: true, "calories": 100 },
  { name: "nacho cheese", isHealthy: false, "calories": 1200 },
  { name: "boring chicken", isHealthy: true, "calories": 500 },
];

const myFilterFn = ({isHealthy, calories}) => isHealthy && calories > 200

data.filter(myFilterFn).map(x => console.log(x))

Я вижу значение этого в более общем виде, но для фильтрации это кажется немного излишним.

Только мои 2 цента.