关于前端数据转换和获取的两个函数

603 阅读6分钟

实战是检验一个前端的最佳实践,如同打怪涨经验升级,在这个漫长的过程中,我们遇到最多的业务层就是数据的处理 和 获取,最近一个项目刚近尾声,总结出了两个函数,一个是对枚举对象的转换处理,另一个是获取数据属性值,各有其用。

$$

$$ api 的作用是深层次获取对象 或是 数组里面的 某个属性值

参数

$$(target, defaultVal, cb)

参数解析

这个$$函数接受三个参数:targetdefaultValcb

  • target:这是你想要创建代理的对象。如果target是一个数组并且cb是一个函数,那么它会找到数组中第一个满足cb函数的元素。如果没有找到满足条件的元素,target会被设置为一个空对象。
  • defaultVal:这是当你试图访问target对象的一个不存在的属性时返回的默认值。
  • cb:这是一个可选的回调函数,它用于在target是一个数组时,找到第一个满足该函数的元素。如果cb不是一个函数或者没有提供,那么target将不会被修改。

$$ 使用场景一:获取json数组某个索引下的json下面的深层及属性值

const jsonArr = [
  { id: 1, name: '张三', age: 18, info: { sex: '男', address: '北京' } },
  { id: 2, name: '李四', age: 19, info: { sex: '女', address: '北京' } },
  { id: 3, name: '王五', age: 20, info: { sex: '男', address: '北京' } }
]
/**
 * 深层次访问 数组
 */

// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值
$$(jsonArr)['0.info'] // {sex: '男', address: '北京'}

// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值下面的 address 值
$$(jsonArr)['0.info.address'] // '北京'

// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值下面的 address 值
$$(jsonArr,'木有访问到值时候的默认值')['0.info.address'] // '北京'

// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值下面的 address 值,没有访问到返回默认值
$$(jsonArr,'木有访问到值时候的默认值')['0.info.phone'] // '木有访问到值时候的默认值'

// 获取 jsonArr 数组下面  索引 满足条件 id =2 的值对象下面的 info 值下面的 address 值
$$(jsonArr,'默认值',(item)=>item.id===2)['info.address'] // '北京'

// 获取 jsonArr 数组下面  索引 满足条件 id =22 的值对象下面的 info 值下面的 address 值,没有找到返回默认值
$$(jsonArr,'默认值',(item)=>item.id===22)['info.address'] // '默认值'

$$ 使用场景二:深层级获取对象下面的属性值

const jsonObj = {
  id: 1,
  name: '张三',
  age: 18,
  info: {
    sex: '男',
    address: '北京'
  }
}

// 获取 jsonObj 对象下面 info 值
$$(jsonObj)['info'] // {sex: '男', address: '北京'}

// 获取 jsonObj 对象下面 info 值下面的 address 值
$$(jsonObj)['info.address'] // '北京'

// 获取 jsonObj 对象下面 info 值下面的 address 值
$$(jsonObj,'木有访问到值时候的默认值')['info.address'] // '北京'

// 获取 jsonObj 对象下面 info 值下面的 address 值,没有访问到返回默认值
$$(jsonObj,'木有访问到值时候的默认值')['info.phone'] // '木有访问到值时候的默认值'

// 获取 jsonObj 对象下面 info 值下面的 phone 值,没有给默认值的情况下
$$(jsonObj)['info.phone'] // undefined

vue项目中,我们知道在 vue 的 template 不能使用 高级语法 ?.,但是你确实想用,因为可以节省你很多时间和让你少写很多代码,让你可以多出很多时间来愉快的摸鱼,$$ 也是可以完成的

<template>
  <div class="hello">
    <!-- <p> {{ jsonObj?.info?.address || "默认值" }} </p> 这样子写链式是不支持的,用 $$ 解决即可-->
    <!-- 获取 jsonObj 对象下面 info 值下面的 address 值 -->
    <p> {{ $$(jsonObj)['info.address'] }} </p>
    <!-- 获取 jsonArr 数组下面  索引 满足条件 id =2 的值对象下面的 info ,当然不建议在 模版内写太多判断-->
    <p> {{ $$(jsonArr,'默认值',i => i.id===2)['info.address'] }} </p>
    <!-- 获取 jsonArr 数组下面  索引 1 的值对象下面的 info 值下面的 address 值 -->
    <p> {{ $$(jsonArr,'默认值')['1.info.address'] }} </p>
  </div>
</template>
<script>
export default {
  data(){
    return {
      jsonArr: [
        {id: 1, name: '张三', age: 18, info: {sex: '男', address: '北京'}},
        {id: 2, name: '李四', age: 19, info: {sex: '男', address: '北京'}},
        {id: 3, name: '王五', age: 20, info: {sex: '男', address: '北京'}},
      ],
      jsonObj: {
        id: 1,
        name: '张三',
        age: 18,
        info: {
          sex: '男',
          address: '北京'
        }
      }
    }
  },
  methods: {
    $$(target, defaultVal, cb){
        if(Array.isArray(target) && typeof cb === 'function'){
            target = target.find(i=>cb(i)) || {}
        }
        return new Proxy(target, {
            get: (target, propKey) => {
                const proKeyArr = propKey.split('.')
                return proKeyArr.reduce((a, b) => a?.[b] || defaultVal, target)
            }
        })
    }
  }
}
</script> 

源码

function $$(target, defaultVal, cb){
    if(Array.isArray(target) && typeof cb === 'function'){
        target = target.find(i=>cb(i)) || {}
    }
    return new Proxy(target, {
        get: (target, propKey) => {
            const proKeyArr = propKey.split('.')
            return proKeyArr.reduce((a, b) => a?.[b] || defaultVal, target)
        }
    })
}

switchData

前端静态数据映射枚举对象 和 数组 互相转换,场景:后端接口返回的数据是枚举对象,前端需要转换成数组,或者前端定义的枚举对象,后端接口需要转换成枚举对象

switchData({
  data = null, 
  sortAlias = 'index', 
  typeAlias = 'type', 
  switchType = 'object', 
  cb = null, 
  mapValueName = '', 
  types = [] 
})

  • data: 输入数据,可以是数组或对象(默认:null)。
  • sortAlias: 用于对输出数组进行排序的属性名(默认:'index')。
  • typeAlias: 用于识别输入数组中每个对象的类型的属性名(默认:'type')。
  • switchType: 输入数据的类型('object'或'array'(默认为'object'))。
  • cb: 一个回调函数,用于在将数据转换为数组之前处理数据(默认值:null)。
  • mapValueName: 用于将输入数组中的值映射到不同属性名的属性名(默认值:")。
  • types: 在转换数据时要过滤的类型数组(默认值:[])。

该函数首先根据switchType属性检查输入数据是数组还是对象。 如果输入数据是一个数组,它将根据types属性过滤该数组,然后使用回调函数cb(如果提供的话)或将mapValueName映射到不同的属性名,将其简化为一个对象。 如果输入数据是一个对象,则它根据types属性过滤对象键,然后使用回调函数cb(如果提供的话)或将mapValueName映射到不同的属性名,将其缩减为一个数组。 最后,该函数根据输入数据类型返回处理过的数组或对象。

switchData 使用场景一:枚举对象转换成指定顺序的数组

const billTypeEnum = {
  9: { label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2 }, 
  4: { label: '收款单', path: '/dhpc/bill/receipt-detail', sort: 0 },
  5: { label: '付款单', path: '/dhpc/bill/pay-detail', sort: 1 },
}
switchData({ data: billTypeEnum, sortAlias: 'sort' })

/* 
 [
  { label: '收款单',     path: '/dhpc/bill/receipt-detail',         sort: 0, type: 4 },
  { label: '付款单',     path: '/dhpc/bill/pay-detail',             sort: 1, type: 5 },
  { label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2, type: 9 }
 ]
*/

如果你说你不想使用 typeAlias 的默认值 type 作为枚举键的key, 如果不是默认值, typeAlias值改成你业务中的 key 值即可, 示例如下:

onst billTypeEnum = {
  9: { label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2 }, 
  4: { label: '收款单', path: '/dhpc/bill/receipt-detail', sort: 0 },
  5: { label: '付款单', path: '/dhpc/bill/pay-detail', sort: 1 },
}
switchData({ data: billTypeEnum, sortAlias: 'sort', typeAlias: 'value' })

/**
 * [
 * { label: '收款单',     path: '/dhpc/bill/receipt-detail',         sort: 0, value: 4 },
 * { label: '付款单',     path: '/dhpc/bill/pay-detail',             sort: 1, value: 5 },
 * { label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2, value: 9 }
 * ]
 */

switchData 使用场景二:枚举对象转换成 指定 types 数组

在这个场景中,不不需要全部转换枚举的每个键值对,你只想通过 types参数 进行过滤指定的而已, 示例如下:

const billTypeEnum = {
  9: { label: '销售出库单', index: 14 }, 
  4: { label: '收款单', index: 3 },
  5: { label: '付款单', index: 2 },
  6: { label: '销售退货单', index: 4 },
  7: { label: '采购入库单',  index: 0 }, 
  8: { label: '采购退货单',  index: 1 },
  11: { label: '其他入库单',  index: 5 },
  12: { label: '其他出库单',  index: 6 },
  13: { label: '成本调价单',  index: 7 },
  19: { label: '应收调整单',  index: 8 },
  26: { label: '应付调整单',  index: 9 },
  28: { label: '组装拆装单',  index: 10 },
  29: { label: '其他收入单',  index: 11 },
  30: { label: '费用单',  index: 12 },
  33: { label: '现金银行转款单', index: 13 }
}
switchData({ data: billTypeEnum, types:[6,12,33] })
/**
 * [
 * { label: '销售退货单',      index: 4,  type: 6 },
 * { label: '其他出库单',      index: 6,  type: 12 },
 * { label: '现金银行转款单',  index: 13, type: 33 }
 * ]
 */

// 如果想自定义返回的数据类型时候,你可以使用 cb 参数 类似 JavaScript里面的 map api

switchData({ data: billTypeEnum, types:[6,12,33], cb: item=>item.label })
// ['销售退货单', '其他出库单', '现金银行转款单']

switchData 使用场景三:JSON 数组 转换成 枚举对象

现有json数组

const jsonArr = [
  { id: 1, name: '张三' },
  { id: 2, name: '李四' },
  { id: 3, name: '王五' }
]

使用 switchData 转换成 枚举对象

// 把json里面的 id 值作为枚举映射 key(typeAlias: 'id')
switchData({ data: jsonArr, switchType:'array', typeAlias: 'id'})

/**
 
  {
    1:{ id: 1, name: '张三' },
    2:{ id: 2, name: '李四' },
    3:{ id: 3, name: '王五' }
  }

 */

如果你想要转换结果数据是这样的:

// {1: '张三', 2: '李四', 3: '王五'}

可以这样做

// 枚举映射 取 json对象 对应的 mapValueName 参数参数传入的 键值
switchData({ data: jsonArr, switchType:'array', typeAlias: 'id', mapValueName: 'name'})
// {1: '张三', 2: '李四', 3: '王五'}

源码

function switchData({data = null, sortAlias = 'index', typeAlias = 'type', switchType = 'object', cb = null, mapValueName = '', types = [] }){
    if(!data) return
    const filterCb = (t)=> !types.length || (types.length && types.includes(t*1))
    if(Array.isArray(data) || switchType === 'array'){ // 数组 转 对象
        return data.filter(i=>filterCb(i[typeAlias])).reduce((p,c)=>{
            p[c[typeAlias]] = typeof cb ==="function" ? cb(c) : mapValueName ? c[mapValueName] : c
            return p
        },{})
    } else { // 对象 转 数组
        return Object.keys(data).filter(i=> filterCb(i)).reduce((p,c)=>{
            const val = {...data[c],[typeAlias]:c*1}
            p[data[c][sortAlias]] = typeof cb ==="function" ? cb(val) : val
            return p
        },[]).filter(Boolean)
    }
}

结语

如果你有更好的想法,欢迎留言

文中若有不准确或错误的地方,欢迎指出