skip to Main Content

I have an array of objects, arr1, where each object has multiple properties. I also have a map, map2, where the keys are strings and the values can be of any type. My goal is to find the matching keys between arr1 and map2.

Here is an example of the code I currently have:

// Assume arr1 is an array of objects, each object having multiple properties
var arr1 = [{Alice: 1, Bob: 2}, {Alice: 2,Charlie: 3, David: 4}, {Charlie: 4,Eve: 5, Frank: 6}];
// Assume map2 is a map, with keys as strings and values of any type
var map2= new Map();
map2.set("Alice", 1);
map2.set("David", 2);
map2.set("Eve", 3);

// Original nested for loop
for (var i = 0; i < arr1.length; i++) {
  for (var [key, value] of map2) {
    for (var name in arr1[i]) {
      if (arr1[i][name] == key) {
          arr1[i][name] = map2.get(key)
      }
    }
  }
}

// Optimized nested for loop
for (var i = 0; i < arr1.length; i++) {
  for (var name in arr1[i]) {
    if (map2.has(name)) {
       arr1[i][name] = map2.get(name)
    }
  }
}

The code works as expected, but I wonder if any further optimizations can be done to improve its efficiency. Currently, I iterate over arr1 and check every property of every object against the key in map2. If a match is found, the matching key is recorded. However, the large amount of arr1 data and the number of map keys are inefficient, and I do not know if there is a better way.

Is there a more efficient approach to achieve the same result? Any suggestions or insights would be greatly appreciated. Thank you!

3

Answers


  1. The iterating map keys seems more efficient since the number of keys is potentially smaller that in the array items. But you can have some optimization here: convert the map’s keys to an array and use it when iterating the array items:

    const arr1 = [{Alice: 1, Bob: 2}, {Alice: 2,Charlie: 3, David: 4}, {Charlie: 4,Eve: 5, Frank: 6}];
    const map2= new Map();
    map2.set("Alice", 1);
    map2.set("David", 2);
    map2.set("Eve", 3);
    
    const keys = [...map2.keys()]; // cache map keys here
    
    arr1.forEach(item => keys.forEach(key => (key in item) && (item[key] = map2.get(key))));
    
    console.log(arr1);
    .as-console-wrapper { max-height: 100% !important; }

    But there’s another way to improve the performance: generate a JS code to update the array:

    const arr1 = [{Alice: 1, Bob: 2}, {Alice: 2,Charlie: 3, David: 4}, {Charlie: 4,Eve: 5, Frank: 6}];
    const map2= new Map();
    map2.set("Alice", 1);
    map2.set("David", 2);
    map2.set("Eve", 3);
    
    // generate an update function
    const functionBody = [...map2.entries()].map(([key,val]) => `('${key}' in item) && (item['${key}'] = ${val})`).join(';');
    console.log(functionBody);
    
    const update = new Function('item', functionBody);
    
    arr1.forEach(update);
    
    console.log(arr1);
    .as-console-wrapper { max-height: 100% !important; }

    And benchmark the approaches.

    Here we add additional array item props and map entries(John Sam Peter Maria Alex Trevor Victory Samuel Carl Steve Bill Joe) and multiply array items by 500.

    Seems the iterating map keys with caching has the same speed as iterating the array keys if the number of keys are the same.

    Anyway you should iterate a smaller key array (if the map contains less entries – iterate it, otherwise iterate the array items’ keys).

    Without caching the speed is awfully 900x slower since every array item you fetch a new map iterator which is expensive plus using a iterator is 6.2x slower in chrome than using an array:

    ` Chrome/120
    ----------------------------------------------------------------
    generated function      1.00x  |  x1000  251  257  294  302  310
    array keys              1.23x  |  x1000  308  325  329  331  335
    map keys                1.37x  |  x1000  345  354  356  357  372
    OP's map keys        1191.24x  |     x1  299  301  306  314  318
    ----------------------------------------------------------------
    https://github.com/silentmantra/benchmark `
    
    const chunk = [{Alice: 1, Bob: 2}, {Alice: 2,Charlie: 3, David: 4}, {Charlie: 4,Eve: 5, Frank: 6}];
    const map2= new Map();
    map2.set("Alice", 1);
    map2.set("David", 2);
    map2.set("Eve", 3);
    
    'John Sam Peter Maria Alex Trevor Victory Samuel Carl Steve Bill Joe'
      .split(' ').forEach(name => chunk.forEach(item => (item[name] = 0, map2.set(name, 1))));
    const arr1 = [];
    let count = 500;
    while(count--) arr1.push(...chunk);
    
    
    // @benchmark OP's map keys
    for (var i = 0; i < arr1.length; i++) {
      for (var [key, value] of map2) {
        for (var name in arr1[i]) {
          if (arr1[i][name] == key) {
              arr1[i][name] = map2.get(key)
          }
        }
      }
    }
    arr1;
    
    // @benchmark array keys
    arr1.forEach(item => Object.keys(item).forEach(key => map2.has(key) && (item[key] = map2.get(key))));
    arr1;
    
    // @benchmark map keys
    const keys = [...map2.keys()];
    arr1.forEach(item => keys.forEach(key => (key in item) && (item[key] = map2.get(key))));
    arr1;
    
    // @benchmark generated function
    const functionBody = [...map2.entries()].map(([key,val]) => "('"+key+"' in item) && (item['"+key+"'] = "+val + ")").join(';');
    const update = new Function('item', functionBody);
    arr1.forEach(update);
    arr1;
    
    /*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));
    .as-console-wrapper { max-height: 100% !important; }
    Login or Signup to reply.
  2. Efficiency mostly is about finding a good balance in between performance and readability/maintainability.

    Thus, the next provided example code targets readability and waits for being performance tested by Alexander’s testing tool.

    The code can be read almost literally …

    arr
      // for each array item ...
      .forEach(item => Object
        // ... get all of an item's own string based keys ..
        .keys(item)
        // ... and then, for each of an item's key ...
        .forEach(key => {
          // .... check whether a map entry
          //      does exist for this key ...
          if (map.has(key)) {
    
            // ... if so, assign the stored value
            //     to its related item's property.
            item[key] = map.get(key);
          }
        })
      );
    

    … and could be directly transformed into …

    for (const item of arr) {
      for (const key in item) {
        if (map.has(key)) {
    
          item[key] = map.get(key);
        }
      }
    }
    

    … in case one makes use of for...of (iterable) and for...in (object)

    const map = new Map([
      ["Alice", 1],
      ["David", 2],
      ["Eve", 3],
    ]);
    const arr = [{
      Alice: 1,
      Bob: 2,
    }, {
      Alice: 2,
      Charlie: 3,
      David: 4,
    }, {
      Charlie: 4,
      Eve: 5,
      Frank: 6,
    }];
    console.log({ 'before ... arr': arr });
    
    // arr
    //   .forEach(item => Object
    //     .keys(item)
    //     .forEach(key => {
    //       if (map.has(key)) {
    // 
    //         item[key] = map.get(key);
    //       }
    //     })
    //   );
    
    for (const item of arr) {
      for (const key in item) {
        if (map.has(key)) {
    
          item[key] = map.get(key);
        }
      }
    }
    console.log({ 'after ... arr': arr });
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    Login or Signup to reply.
  3. This is improvised Answers for your question.

    const arr = [{ Alice: 1, Bob: 2 }, { Alice: 2, Charlie: 3, David: 4 }, { Charlie: 4, Eve: 5, Frank: 6 }];
    const updates = new Map([["Alice", 1], ["David", 2], ["Eve", 3]]);
    const updateFunc = new Function('item', [...updates.entries()].map(([key, val]) => `('${key}' in item) && (item['${key}'] = ${val})`).join(';'));
    arr.forEach(updateFunc);
    
    console.log('arr',arr)
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search