我有两个对象,一个描述一个位置的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
为空。
Additional code/workbench on 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)
我仍然几乎必须秘密地叫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-Programmer将如何处理这个问题?
我愿意接受用lodash-fp
或rambda
(或任何我能理解的有好的文档)编写的解决方案,因为这些解决方案可能会提供更简洁的代码,因为它们更注重功能/更易于使用比标准lodash
。
答案 0 :(得分:2)
Lodash
以下是使用_.flow()
的解决方案:
_.values()
,_.flatten()
和_.compact()
将要素转换为数组(在building
时忽略undefined
)。id
转换为_.map()
个数组。_.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>
拉姆达
undefined
获取值,展平并过滤R.identity
。id
获得R.map
道具。R.props
从prices
获取id值
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>
答案 1 :(得分:1)
这是我使用Ramda的建议。
我建议将问题分解为较小的功能:
getImprovementIds
getBuildingId
getPriceIds
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
一旦有了价格ID列表,就可以轻松地将该列表转换为价格列表:
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>