简体   繁体   中英

Efficiently converting JavaScript key and value arrays to object

I need to turn two JavaScript arrays into a list of objects. One of the input arrays represents the keys of the output object and the other contains its values (alongside some other information, not relevant to this question).

Example data:

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

Desired output for above:

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

(This is just an example, my actual data comes from REST responses and can vary in content and length. I'm working with a Vue app that displays tabular data.)

My existing code, below, works for small amounts of data but makes all browsers crash or hang for larger amounts of data.

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 as few as a couple thousand results, this takes minutes to run. How can I make it faster? Is there some operation I'm missing that will allow me to avoid the nested for loop?

The easiest way maybe to .map the values into key-value tuples and call Object.fromEntries on it:

 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])) ) )

You can get rid of the inner loop if you hardcode the properties instead of looking at theKeys , but I doubt you'd want that. The only thing you don't really need is the flatMap . Most of the generic array methods aren't known for their speed, anyway ( forEach is usually slower than a plain for loop, for example).

FWIW, this seems to perform fine:

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);
}

I tested it with 11k items and it ran in about 5 miliseconds in Chrome. With 90k items, it still only took about 30ms.

You can forego the flatMap which should save you some performance, just do everything with plain loop:

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)
    }
}

EDIT: micro-optimizations

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)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM