Разделение / вложение _.flow с помощью lodash (или ramda) - PullRequest
0 голосов
/ 27 января 2019

У меня есть два объекта, один описывает features местоположения, другой описывает prices этих функций.

features = {
  improvements: [...] // any array of many id's
  building: {} // only one id, may be undefined
}
prices = {
  id_1: 10,
  ...
}

Я хочу перебрать features и сопоставить всеprices. Иногда features.building будет undefined, иногда features.improvements будет пустым .

Дополнительный код / ​​верстак на repl.it

Я могу сделать это с lodash следующим образом:

result = _(features.improvements)
  .map(feature => prices[feature.id])
  .concat(_.cond([
    [_.isObject, () => prices[features.building.id]]
  ])(features.building)) // would like to clean this up
  .compact()
  .value();

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

result = _.flow([
  _.partialRight(_.map, feature => prices[feature.id]),
  _.partialRight(_.concat, _.cond([
    [_.isObject, () => prices[features.building.id]]
  ])(features.building)),
  _.compact,
])(features.improvements)

Iмне все еще приходится почти тайно вызывать features.building средний поток, что мне неудобно.

Что я хотел бы получить, это (псевдокодировано):

flow([
  // maybe need some kind of "split([[val, funcs], [val, funcs]])?
  // the below doesn't work because the first
  // flow's result ends up in the second

  // do the improvement getting
  flow([
    _.map(feature => prices[feature.id])
  ])(_.get('improvements')),

  // do the building getting
  flow([
    _.cond([
      [_.isObject, () => [argument.id]]
    ])
  ])(_.get('building')),

  // concat and compact the results of both gets
  _.concat,
  _.compact,
])(features); // just passing the root object in

Возможно ли это?Как к этому подойдет более опытный FP-программист?

Я открыт для решений, написанных с lodash-fp или rambda (или с любыми документами, которые я могу попытаться понять), поскольку они, вероятно, дают более чистый кодпотому что они более функционально ориентированы / карри, чем стандартные lodash.

Ответы [ 2 ]

0 голосов
/ 27 января 2019

Вот мое предложение с использованием Рамды.

Я предлагаю разбить проблему на более мелкие функции:

  1. Получить идентификаторы цен для всех улучшений: getImprovementIds
  2. Получить идентификатор цены здания: getBuildingId
  3. Получить идентификаторы цены для всего: getPriceIds
  4. Получить цену по данной цене ID: getPrice

Примеры

getImprovementIds(features); //=> ['id_1', 'id_2', 'id_3']
getBuildingIds(features);    //=> ['id_5']
getPriceIds(features);       //=> ['id_1', 'id_2', 'id_3', 'id_5']
getPrice(prices, 'id_2');    //=> 20
getPrice(prices, 'foo');     //=> 0

Если у вас есть список идентификаторов цен, его легко преобразовать в прайс-лист:

map(getPrice(prices), ['id_1', 'id_2', 'id_3', 'id_5']); //=> [10, 20, 0, 50]

Полный пример

const {propOr, ifElse, hasPath, path, always, compose, sum, map, flip, converge, of, concat} = R;

const features = {
  improvements: ['id_1', 'id_2', 'id_3'],
  building: {
    id: 'id_5'
  }
};

const prices = {
  id_1: 10,
  id_2: 20,
  id_5: 50
};

/**
 * Take a features object and return the price id of all improvements.
 * @param {object} features
 * @return {array} array of ids
 */
const getImprovementIds = propOr([], 'improvements');

/**
 * Take a features object and return the price id of the building (if any)
 * @param {object} features
 * @return {array} array of ids
 */
const getBuildingId =
  ifElse(hasPath(['building', 'id']),
    compose(of, path(['building', 'id'])),
    always([]));

/**
 * Take a features object and returns all price id of all improvements and of the building (if any)
 * @param {object} features
 * @return {array} array of ids
 */
const getPriceIds = converge(concat, [getImprovementIds, getBuildingId]);

/**
 * Take a prices object and a price id and return the corresponding price
 *
 * @example
 * getPrice(prices, 'id_2'); //=> 20
 *
 * @param {object} prices
 * @param {string} id
 * @return {number}
 */
const getPrice = flip(propOr(0));

const getPriceList = (prices, features) =>
  map(getPrice(prices), getPriceIds(features));

console.log(
  getPriceList(prices, features)
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
0 голосов
/ 27 января 2019

Lodash

Вот решение, которое использует _.flow():

  1. Преобразование объектов в массив с использованием _.values(), _.flatten()_.compact() (игнорировать building при undefined).
  2. Преобразовать в массив id s с помощью _.map().
  3. Получить значения с помощью _.at().

const { values, flatten, compact, partialRight: pr, map, partial, at } = _;

const fn = prices => _.flow([
  values,
  flatten,
  compact,
  pr(map, 'id'),
  partial(at, prices)
])

const prices = {
  i_1: 'cost_i_1',
  i_2: 'cost_i_2',
  i_3: 'cost_i_3',
  i_4: 'cost_i_4',
  b_1: 'cost_b_1',
};

const features = {
  improvements: [
    {id: 'i_1'},
    {id: 'i_2'},
    {id: 'i_3'},
    {id: 'i_4'},
  ],
  building: {
    id: 'b_1'
  },
};

const result = fn(prices)(features);

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

lodash / fp

const { values, flatten, compact, map, propertyOf } = _;

const fn = prices => _.flow([
  values,
  flatten,
  compact,
  map('id'),
  map(propertyOf(prices))
])

const prices = {"i_1":"cost_i_1","i_2":"cost_i_2","i_3":"cost_i_3","i_4":"cost_i_4","b_1":"cost_b_1"};
const features = {"improvements":[{"id":"i_1"},{"id":"i_2"},{"id":"i_3"},{"id":"i_4"}],"building":{"id":"b_1"}};

const result = fn(prices)(features);

console.log(result);
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>

Рамда

  1. Получить значения, сгладить и отфильтровать undefined с использованием R.identity.
  2. Получите реквизиты id с R.map.
  3. Используйте перевернутый R.props, чтобы получить значения идентификаторов из prices

const { pipe, values, flatten, filter, identity, map, prop, flip, props } = R;

const propsOf = flip(props);

const fn = prices => pipe(
  values,
  flatten,
  filter(identity),
  map(prop('id')),
  propsOf(prices)
);

const prices = {"i_1":"cost_i_1","i_2":"cost_i_2","i_3":"cost_i_3","i_4":"cost_i_4","b_1":"cost_b_1"};
const features = {"improvements":[{"id":"i_1"},{"id":"i_2"},{"id":"i_3"},{"id":"i_4"}],"building":{"id":"b_1"}};

const result = fn(prices)(features);

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