我正在尝试使用 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
}
]
无法保证任何给定地点将有多少个地址组件,或者是否有任何地址组件。无法保证哪些类型会被代表,哪些类型不会被代表。
我想编写返回第一个地址组件的 long_name 的代码,该组件的字段 R.get(R.lensPath('types', '0'))
为 '邻居'
(如果存在)locality
,否则,则为 postal_town
、administrative_area_level_2
、administrative_area_level_1
,然后 国家
。
所以我从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)]
虽然确实可以通过将所有 native .reduce
和 .map
替换为 R.map
来使这稍微更惯用。 code>/R.reduce
这并没有真正解决根本问题。
1) 即使找到结果后,这也会迭代列表中的每个成员。
2) 生成的结构仍然需要迭代(例如查找)才能真正找到最紧边界。
纯函数式、最好是惰性实现会是什么样子? Ramda 的哪些功能可以派上用场?我可以以某种方式使用镜头吗?功能组成?还有别的吗?
可以将原生 map
/reduce
与 ramda 混合搭配吗?当然,只要有可能, native 调用就比库调用更好吗?
最佳答案
一种方法是创建 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))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.23.0/ramda.min.js"></script>
关于javascript - Ramda JS 获得最严格地理限制的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44131662/