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

Posted

技术标签:

【中文标题】高效地将 JavaScript 键和值数组转换为对象【英文标题】:Efficiently converting JavaScript key and value arrays to object 【发布时间】:2021-03-29 10:08:20 【问题描述】:

我需要将两个 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 循环?

【问题讨论】:

this.theValue的大小是多少,它也是vue中的计算属性吗? 情况不同。如果是 7 岁或 18 岁,一切都很好。如果是 20,429,我就有问题了。它不是计算出来的,它是从远程服务器返回的数据。 在 flatMap 之前包含 console.time('test'),在返回之前包含 console.timeEnd('test'),以查看经过的时间。 我将其范围缩小到对theValues 的调用,这实际上是我正在使用的组件的一个道具。我将其替换为Math.random(),一切都快如闪电。我认为现在是时候提出一个新问题了,更关注 Vue。 @WashingtonGuedes 仅供参考,以防您仍然感兴趣,我最终确实提出了后续问题:***.com/questions/65569982/… 【参考方案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)
    

【讨论】:

【参考方案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);

我用 11k 个项目对其进行了测试,它在 Chrome 中运行了大约 5 毫秒。对于 90k 个项目,它仍然只需要大约 30 毫秒。

【讨论】:

此代码在我的应用程序中产生正确的结果,但在 29851 行上运行需要 255388 毫秒。发生了一些奇怪的事情。【参考方案3】:

最简单的方法可能是将.map 值转换为键值元组并在其上调用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]))
  )
)

【讨论】:

以上是关于高效地将 JavaScript 键和值数组转换为对象的主要内容,如果未能解决你的问题,请参考以下文章

如何将 JSON 属性值转换为键和值数组

Javascript映射函数:向对象添加新键和值[重复]

使用javascript删除与对象的另一个键和值对相对应的对象数组中的重复项

将两个数组(键和值)合并为一个对象[重复]

未能将键和值添加到javascript对象

高效的方法将键和值添加到scala中的Set Map