笔试代码题
构造树形结构
前端使用树形结构的数据很常见,比如展示层级的Tree 树形控件.
;[
{
id: 1,
name: 'Item 1',
parentId: 8,
children: [
{
id: 2,
name: 'Item 1.1',
parentId: 1,
children: [],
},
],
},
]
但是有时候给我们的数据是这样的,需要我们将数据处理成树形结构那样.
;[
{ id: 6, name: 'Item 2.2', parentId: 1 },
{ id: 1, name: 'Item 1', parentId: 8 },
{ id: 2, name: 'Item 1.1', parentId: 1 },
]
这里使用了Map来记录每一项数据 id 与之对应的关系,默认给每一项加入了 children 属性,用于存储子节点.
遍历源数据拿到每一项的 id 与 parentId,根据 parentId 找到对应的父节点,然后将当前节点添加到父节点的 children 属性中.这里将没有对应父节点的节点添加到根节点上,可以根据具体的业务逻辑修改.
function buildTree(flatArray, idKey = 'id', parentIdKey = 'parentId') {
const itemMap = new Map()
const tree = []
flatArray.forEach((item) => {
const { [idKey]: id } = item
const node = { ...item, children: [] }
itemMap.set(id, node)
})
flatArray.forEach(({ [idKey]: id, [parentIdKey]: parentId }) => {
const currentNode = itemMap.get(id)
const parentNode = itemMap.get(parentId)
parentNode ? parentNode.children.push(currentNode) : tree.push(currentNode)
})
return tree
}
数字进行千分位格式化.
类似下面这样每三位对数字进行转换
- 1234567 => 1,234,567
- 1234567.891245 => 1,234,567.891,245
这个用正则很快就能解决掉,但是对正则不熟悉的有点麻烦.这里不使用正则来进行分割.
function formatPrice(price) {
const [integerPart, decimalPart] = price.toString().split('.')
const int = integerPart.split('').reduceRight((prev, next, index) => {
return (index % 3 ? next : next + ',') + prev
})
const dec = decimalPart?.split('').reduce((prev, next, index) => {
return (index % 3 ? prev : prev + ',') + next
})
return dec ? `${int}.${dec}` : int
}
console.log(formatPrice(1234567))
console.log(formatPrice(1234567.891245))
console.log(formatPrice(0.891245))
console.log(formatPrice(0.0))
这里分别对整数和小数进行格式化.利用了 reduce 方法以及它里面回调函数的 index 来判断是否需要添加逗号.
获取 url 参数
function getAllUrlParams(url, query) {
const queryString = url.split('?')[1] // 提取查询字符串
const params = new URLSearchParams(queryString)
// return params.getAll(query)
return params.get(query)
}
// 示例
const url =
'https://example.com/page?name=JohnDoe&age=25&country=US&name=JohnDoe1'
console.log(getAllUrlParams(url, 'name'))
lodash.get
function get(obj, path, defaultValue) {
if (!path) return obj
const pathKey = Array.isArray(path) ? path : _basePath(path)
let result = obj
for (const k of pathKey) {
result = result[k]
if (!result) return defaultValue
}
return result
}
// 将 'a[0].b.c' => ['a', '0', 'b', 'c'] 处理成数组形式方便处理
function _basePath(path) {
return path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
}
const object = { a: [{ b: { c: 3 } }] }
console.log(get(object, 'a[0].b.c'))
// => 3
console.log(get(object, ['a', '0', 'b', 'c']))
// => 3
console.log(get(object, 'a.b.c', 'default'))
// => 'default'
es6 里面的可选链可以替代这个方法了
lodash.set
设置 object 对象中对应 path 属性路径上的值,如果 path 不存在,则创建。 缺少的索引属性会创建为数组,而缺少的属性会创建为对象。 使用_.setWith 定制 path 创建。
function set(obj, path, value) {
if (!path) return obj
const pathKey = Array.isArray(path) ? path : _basePath(path)
let cur = obj
for (const [i, v] of pathKey.entries()) {
if (i === pathKey.length - 1) {
cur[v] = value
return
}
if (typeof cur[v] === 'undefined') {
cur[v] = /[0-9]/.test(v) ? [] : {}
}
cur = cur[v]
}
}
// 将 'a[0].b.c' => ['a', '0', 'b', 'c'] 处理成数组形式方便处理
function _basePath(path) {
return path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
}
const object = { a: [{ b: { c: 3 } }] }
set(object, 'a[0].b.c', 4)
console.log(object.a[0].b.c)
// => 4
set(object, ['x', '0', 'y', 'z'], 5)
console.log(object.x[0].y.z)
// => 5
console.log(/[0-9]/.test(1))
这个 set 和 get 方法,都需要对传入的参数进行处理成数组形式方便后面使用,同时需要一个额外的变量保存当前节点.
set 方法还需要对属性和索引不存在的情况额外判断赋值.