- 反思1
最近发现思维能力下降的厉害,写了一个很挫的数据转换。
需求是遍历一个二维数组,根据json对象(有可能需要合并),生成一个新的一维数组。
这个合并,让我陷入了思维困局,固定思维觉得需要去查找转换的数组,是否已经有这个对象了,已经有了就合并,没有就生成新的。
导致算法复杂度On了。架构师连夜帮忙写了一个高性能转换,换上之后效果飞增。不得不拍手叫绝。
- 效果差异
先来看下优化之前的效果:
高性能数据转换后的js执行时间:
差别不就有了吗,就一个算法问题节省了几乎百分百的时间啊!
- 需求描述
再来说下具体需求:
| time | {name: 'count', tags: {group: 'test1',type: 'test2'}} | {name: 'sum_count', tags: {group: 'test3',type: 'test4'}} | {name: 'count', tags: {group: 'test6',type: 'test5'}} | {name: 'sum_count', tags: {group: 'test6',type: 'test5'}} |
|---|---|---|---|---|
| 2023-03-31 | 1 | 23 | 423 | 34 |
| 2023-03-30 | 1 | 23 | 423 | 34 |
| 2023-03-29 | 1 | 23 | 423 | 34 |
| ... | ... | ... | ... | ... |
把上面这个表格转成下面这种格式:
| time | group | type | count | sum_count |
|---|---|---|---|---|
| 2023-03-31 | test1 | test2 | 1 | 23 |
| 2023-03-31 | test3 | test4 | 423 | 34 |
| 2023-03-30 | test1 | test2 | 1 | 23 |
| 2023-03-30 | test3 | test4 | 423 | 34 |
| 2023-03-29 | test1 | test2 | 1 | 23 |
| 2023-03-29 | test3 | test4 | 423 | 34 |
- 反思2
当然这个是具象之后的需求。我一开始就没有把需求具象,总觉得把需求用这种方式表达出来要花很多时间,确实要花十几分钟,才能把需求具象成这样(加上写和想的时间)。但是有了具象的东西之后就好做思考了,而不是在脑子里把概念的一个东西转成另一个东西。非常考验脑力。还是采用这种方式,对脑子更友好一些。宁愿多花点时间吧。
- 具体转换过程
挫的版本(完全就是思维的灾难啊)
rotateRowColumn(rotateColumns) {
if (!rotateColumns.length && !this.dimensionEntity.hasTags) {
return
}
const item = {}
rotateColumns.forEach(c => {
item[c.key || c.field] = ''
})
let temResults = [],
results = []
this.dimensions.forEach(dim => {
if (
dim.type === NUMBER &&
dim.tags &&
Object.prototype.toString.call(dim.tags) === '[object Object]'
) {
if (!temResults.find(t => this.findItemByTag(t, dim.tags))) {
const newItem = { ...item }
Object.keys(dim.tags).forEach(t => {
newItem[t + HYPHEN + TAG_ORDINAL] = dim.tags[t]
})
temResults.push(newItem)
}
}
})
if (
this.dimensions[0].type !== TIME &&
this.dimensions[0].type !== ORDINAL
) {
this.source.forEach(sourceElement => {
sourceElement.forEach((val, index) => {
const dim = this.dimensions[index]
const obj = temResults.find(t => this.findItemByTag(t, dim.tags))
obj[dim.name + HYPHEN + dim.type] = val
})
})
return temResults
}
this.source.forEach(sourceElement => {
sourceElement.forEach((val, index) => {
const dim = this.dimensions[index]
const dim0 = this.dimensions[0]
if (dim.type === TIME || dim.type === ORDINAL) {
const temp = temResults.map(i => {
return {
...i,
[dim.name + HYPHEN + dim.type]: val
}
})
results.push(...temp)
return
}
const obj = results.find(
t =>
this.findItemByTag(t, dim.tags) &&
t[dim0.name + HYPHEN + dim0.type] === sourceElement[0]
)
obj[dim.name + HYPHEN + dim.type] = val
})
})
return results
}
牛叉的版本
export default class DatasetConverter {
static convert(dataset) {
if (
dataset === null ||
dataset.dimension === null ||
dataset.source === null
) {
return []
}
const columnInfos = this.parseDatasetDimensions(dataset.dimensions)
const jsonObjects = []
for (const row of dataset.source) {
const rowToJsonObjects = this.parseRowToJsonObjects(row, columnInfos)
jsonObjects.push(...rowToJsonObjects)
}
return jsonObjects
}
static parseDatasetDimensions(dimensions) {
const columnInfos = []
for (let i = 0; i < dimensions.length; i++) {
const dimension = dimensions[i]
//用了一个ColumnInfo的结构,没有任何冗余代码
const columnInfo = new ColumnInfo(dimension)
columnInfos.push(columnInfo)
}
return columnInfos
}
static parseRowToJsonObjects(row, columnInfos) {
const objectMap = {}
const commonProperties = {}
for (let i = 0; i < row.length; i++) {
const value = row[i]
const ci = columnInfos[i]
if (ci.common) {
commonProperties[ci.dimension.getName()] = value
continue
}
//用tags做为key,设了一个小小的缓存,并没有多大内存开销
let jsonObject = objectMap[ci.stringTags]
//没转换过,就是新的
if (!jsonObject) {
jsonObject = { ...ci.jsonTags }
objectMap[ci.stringTags] = jsonObject
}
jsonObject[ci.dimension.getName()] = value
}
for (const json of Object.values(objectMap)) {
Object.assign(json, commonProperties)
}
//最后把map的values拿出来就是想要的数组了呀
return Object.values(objectMap)
}
}
class ColumnInfo {
constructor(dimension) {
this.dimension = new Dimension(dimension)
this.common = this.isCommonDimension()
if (!this.common) {
this.stringTags = this.dimension.getStringTags()
this.jsonTags = this.dimension.getTags()
}
}
isCommonDimension() {
if (!this.dimension.getTags()) {
return true
}
return false
}
}
class Dimension {
name
type
tags
constructor({ name, type, tags } = {}) {
this.name = name
this.type = type
this.tags = tags
}
getName() {
return this.name + HYPHEN + this.type
}
getStringTags() {
return JSON.stringify(this.tags)
}
getTags() {
if (isEmptyObject(this.tags)) return null
const tags = {}
for (const tag in this.tags) {
tags[tag + HYPHEN + TAG_ORDINAL] = this.tags[tag]
}
return tags
}
}
巧用数组下标和用tags为key的map就搞定了查找的问题,功力深厚!佩服佩服!反观自己写的代码,真是思维灾难,一个程序员怎么能写出这么流水账的代码呢???哎!