我正在尝试使用Google Places API来获取我所在位置的地名。
返回的数据结构具有以下类型:
descriptor1: 'street number' | 'neighborhood' | 'postcode' | 'route' | 'locality' | 'postal_town' | 'administrative_area_level_2' | 'administrative_area_level_1' | 'country'
places: [
{
address_components: [{
long_name: 'string',
short_name: 'string',
types: {
0: descriptor1,
1?: descriptor2
}
}],
other_fields not relevant here
}
]
无法保证任何给定地点将拥有多少个地址组件,或者其中任何地址组件是否有任何地址组件。无法保证将会和不会表示哪些类型。
我想编写返回第一个address_component的long_name的代码,如果其中R.get(R.lensPath('types', '0'))
为'neighborhood'
,则locality
的字段为postal_town
,然后administrative_area_level_2
,{ {1}},然后是administrative_area_level_1
,然后是country
。
所以我从R.pluck('address_components', places)
开始。现在我可以构造一个对象,将列表缩小到一个对象,将我感兴趣的每个键中的第一个插入到对象中,然后找到一个值。类似的东西:
const interestingTypes = ['neighborhood', 'locality', 'postal_town', 'administrative_area 2', 'administrative_area_1', 'country']
const res = R.mergeAll(R.pluck('address_components', places).map((addressComponentList) => addressComponentList.reduce((memo, addressComponent) => {
if (interestingTypes.indexOf(addressComponent.types[0]) !== -1) {
if (!memo[addressComponent.types[0]]) {
memo[addressComponent.types[0]] = addressComponent.long_name
}
}
return memo
},{})))
res[R.find((type) => (Object.keys(res).indexOf(type) !== -1), interestingTypes)]
虽然确实可以通过.reduce
/ .map
替换所有原生R.map
和R.reduce
,但实际上并未解决这个问题。根本问题。
1)即使在找到结果之后,这也会遍历列表中的每个成员。
2)生成的结构仍然需要迭代(例如使用find)来实际找到最严格的边界。
这个纯粹的功能,最好是懒惰的实现是什么样的? Ramda的哪些功能可以派上用场?我可以用某种方式使用镜头吗?功能构成?还有别的吗?
将原生的map
/ reduce
与ramda混合搭配是否可以?当然,本地调用尽可能比库调用更好吗?
答案 0 :(得分:1)
一种方法是创建R.reduceRight
的惰性版本:
const lazyReduceR = R.curry((fn, acc, list) => {
function _lazyReduceR(i) {
return i === list.length
? acc
: fn(list[i], () => _lazyFoldR(i + 1))
}
return _lazyReduceR(0)
})
然后可以使用它来创建一个函数,该函数将找到(非空)列表的最小元素,并具有已知的下限:
const boundMinBy = R.curry((byFn, lowerBound, list) =>
lazyReduceR((x, lzMin) => {
if (byFn(x) === lowerBound) {
return x;
} else {
const min = lzMin()
return byFn(x) < byFn(min) ? x : min
}
}, list[0], R.tail(list)))
如果遇到下限,则递归停止并立即返回该结果。
在boundMinBy
可用的情况下,我们可以创建地址类型的查找表来对订单值进行排序:
const sortOrder = {
neighborhood: 0,
locality: 1,
postal_town: 2,
administrative_area_level_2: 3,
administrative_area_level_1: 4,
country: 5
}
以及将为给定地址组件生成排序顺序值的函数:
const sortOrderOfAddress = address => sortOrder[address.types[0]]
然后我们可以用管道完全组合它,例如:
const process = R.pipe(
R.prop('places'),
R.chain(R.pipe(
R.prop('address_components'),
R.unless(
R.isEmpty,
R.pipe(
boundMinBy(sortOrderOfAddress, 0),
R.prop('long_name'),
R.of
)
)
))
)
上面使用 R.chain
来连接所有地点的地址,并过滤掉address_components
为空的地方的任何地址。
如果您想使用某些数据对其进行测试,我在下面的代码段中添加了一个示例。
const lazyReduceR = R.curry((fn, acc, list) => {
function _lazyReduceR(i) {
return i === list.length
? acc
: fn(list[i], () => _lazyReduceR(i + 1))
}
return _lazyReduceR(0)
})
const boundMinBy = R.curry((byFn, lowerBound, list) =>
lazyReduceR((x, lzMin) => {
if (byFn(x) === lowerBound) {
return x;
} else {
const min = lzMin()
return byFn(x) < byFn(min) ? x : min
}
}, list[0], R.tail(list)))
const sortOrder = {
neighborhood: 0,
locality: 1,
postal_town: 2,
administrative_area_level_2: 3,
administrative_area_level_1: 4,
country: 5
}
const sortOrderOfAddress = address => sortOrder[address.types[0]]
const process = R.pipe(
R.prop('places'),
R.chain(R.pipe(
R.prop('address_components'),
R.unless(
R.isEmpty,
R.pipe(
boundMinBy(sortOrderOfAddress, 0),
R.prop('long_name'),
R.of
)
)
))
)
////
const data = {
places: [{
address_components: [{
long_name: 'a',
types: ['country']
}, {
long_name: 'b',
types: ['neighborhood']
}, {
long_name: 'c',
types: ['postal_town']
}]
}, {
address_components: [{
long_name: 'd',
types: ['country']
}, {
long_name: 'e',
types: ['locality']
}, {
long_name: 'f',
types: ['administrative_area_level_2']
}]
}]
}
console.log(process(data))
&#13;
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.23.0/ramda.min.js"></script>
&#13;
答案 1 :(得分:0)
将原生地图/简化与ramda混合搭配是否可以?
绝对。但是你还必须考虑将它们结合起来所涉及的认知成本。
当然,本地调用尽可能比库调用更好?
更好的方法?库函数旨在避免本机函数规范中的一些不幸的复杂性。
此外,当Ramda的核心功能被编写时,它们的性能明显优于本地同行。随着本机引擎的进步以及Ramda的功能变得越来越复杂,这可能已经发生了变化。但它也很可能没有。