高效地将JavaScript键和值数组转换为对象

3
我需要将两个JavaScript数组转换为对象列表。其中一个输入数组表示输出对象的键,另一个包含其值(以及一些其他信息,与此问题无关)。
示例数据:
let theKeys = ['firstName', 'lastName', 'city'];
let theValues = [{data: [['John', 'Smith', 'New York'],
                         ['Mike', 'Doe', 'Chicago'],
                         ...
                        ],
                 otherStuff: ...}
                ];

上述所需的输出:

output = [{
            firstName: 'John',
            lastName: 'Smith',
            city: 'New York'
          },
          {
            firstName: 'Mike',
            lastName: 'Doe',
            city: 'Chicago',
          },
          ...
         ]

这只是一个例子,我的实际数据来自REST响应,内容和长度都可能会有所不同。我正在使用Vue应用程序显示表格数据。

下面是我的现有代码,适用于少量数据,但对于大量数据将导致所有浏览器崩溃或挂起。

return this.theValues.flatMap(results => {
  let jsonified = [];

  for (let v = 0; v < results.theValues.length; v++) {
    let singleJson = {}; 

    for (let k = 0; k < this.theKeys.length; k++) {
      let key = this.theKeys[k];
      singleJson[key] = results.data[v][k];
    }

    jsonified.push(singleJson);
  }

  return jsonified;
});

对于仅有几千个结果,这需要几分钟才能运行。我该如何使它更快?是否有一些操作可以避免嵌套的for循环?


它是变化的。如果是七或十八,一切都好。但如果是20,429,我就有问题了。这不是计算出来的结果,而是从远程服务器返回的数据。 - SOLO
此外,this.theKeys.length 看起来很奇怪... theValues 来自同一响应,但不包括 theKeys?我认为你的问题不是性能本身,而是 Vue 中的某些竞争条件。 - Washington Guedes
1
在flatMap之前加入console.time('test'),并在返回之前加入console.timeEnd('test'),以查看经过的时间。 - Washington Guedes
1
我将问题缩小到了对theValues的调用上,它实际上是我正在工作的组件的一个属性。我用Math.random()替换了它,然后一切都变得非常快。我认为现在是时候提出一个更专注于Vue的新问题了。 - SOLO
1
@WashingtonGuedes 如果你还感兴趣的话,我想告诉你一声,我最终确实提出了那个后续问题:https://stackoverflow.com/questions/65569982/how-can-i-repeatedly-access-a-vue-prop-without-tanking-performance - SOLO
显示剩余16条评论
3个回答

3

最简单的方法可能是将值映射为键-值元组,并在其上调用Object.fromEntries

const theKeys = ['firstName', 'lastName', 'city'];
const theValues = [{
  data: [
    ['John', 'Smith', 'New York'],
    ['Mike', 'Doe', 'Chicago']
  ]
}];

console.log(
  theValues[0].data.map(e =>
    Object.fromEntries(e.map((e,i) => [theKeys[i], e]))
  )
)


2
如果您硬编码属性而不查看theKeys,则可以摆脱内部循环,但我怀疑您不想这样做。您真正不需要的是flatMap。大多数通用数组方法本来就不以速度著称,(例如,forEach通常比普通的for循环慢)。FWIW,这似乎表现良好:
let result = [];
for (let i = 0; i < theValues[0].data.length; i++) {
    let resultObj = {};
    for (let j = 0; j < theKeys.length; j++) {
        resultObj[theKeys[j]] = theValues[0].data[i][j];
    }
    result.push(resultObj);
}

我在Chrome浏览器中测试了11000个项目,它只用了大约5毫秒。当有90000个项目时,仍然只需要大约30毫秒。


1
这段代码在我的应用程序中产生了正确的结果,但运行 29851 行需要 255388 毫秒。有些奇怪的情况正在发生。 - SOLO

1
您可以放弃使用flatMap,这样可以节省一些性能,只需使用普通循环完成所有操作即可:
const result = []

for (const v of theValues) {
    for (const entry of v.data) {
        const obj = {}
        for (let i = 0; i < entry.length; i++) {
            obj[theKeys[i]] = entry[i]
        }
        result.push(obj)
    }
}

编辑:微小的优化

const result = []
const keysLength = theKeys.length

for (let i = theValues.length - 1; i >= 0; i--) {
    const data = theValues[i].data
    for (let j = data.length - 1; j >= 0; j--) {
        const entry = data[j]
        const obj = {}

        for (let k = keysLength - 1; k >= 0; k--) {
            obj[theKeys[k]] = entry[k]
        }
        result.push(obj)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接