Home > Enterprise >  How to improve my left-join syntax in javascript?
How to improve my left-join syntax in javascript?

Time:03-28

So I have the following function that performs an equivalent of right-join using primary keys pKeys in two arrays of objects.

This function works just fine but I want to know if there is a way to improve the performance of this or if there is a better way to accomplish this.

let arrayA = [{id: 1, name: 'ghasem'},
              {id: 2, name: 'ahmad'},
              {id: 43, name: ',mahmoud'},
              {id: 5, name: 'ola'}];
let arrayB = [{id: 1, leader: 'brittney', perf: 4.3},
              {id: 2, leader: 'brian', perf: 9.2, sales: 45},
              {id: 43, leader: 'steven', perf: 0.3},
              {id: 7, leader: 'joe', perf: 5.4}];
pKey = 'id'; //the primary key used for joining

for (element of arrayA) {
  for (other of arrayB) {
    if (element[pKey] == other[pKey]) {
      for (key in other) element[key] = other[key];
    }
  }
}

console.log(arrayA);

CodePudding user response:

We can explore a better one, but it's obvious that by using a HashMap you can save one nested loop used for lookup and using spread operator you can save another loop.

let arrayA = [
    { id: 1, name: "ghasem" },
    { id: 2, name: "ahmad" },
    { id: 43, name: ",mahmoud" },
    { id: 5, name: "ola" },
];
let arrayB = [
    { id: 1, leader: "brittney", perf: 4.3 },
    { id: 2, leader: "brian", perf: 9.2, sales: 45 },
    { id: 43, leader: "steven", perf: 0.3 },
    { id: 7, leader: "joe", perf: 5.4 },
];
pKey = "id"; //the primary key used for joining

const rightMap = new Map();
arrayB.forEach((row) => {
    rightMap.set(row[pKey], row);
});

const joinedData = [];

for (element of arrayA) {
    let row = {};
    if (rightMap.has(element[pKey])) {
        row = rightMap.get(element[pKey]);
    }
    joinedData.push({ ...element, ...row });
}

console.log(joinedData);

CodePudding user response:

You can reduce the complexity to linear O(n m) where n is the values in arrayA and m the values in arrayB.

  1. Index arrayB by the id property for easier lookup: O(m) operation.
  2. Iterate over arrayA (O(n)) and look items up from the map (O(1)) then merge the two using Object.assign:

/* helper */
const toMap = (indexBy, data) =>
  new Map(data.map(item => [item[indexBy], item]));
/* /helpers */

let arrayA = [{id: 1, name: 'ghasem'},
              {id: 2, name: 'ahmad'},
              {id: 43, name: ',mahmoud'},
              {id: 5, name: 'ola'}];
let arrayB = [{id: 1, leader: 'brittney', perf: 4.3},
              {id: 2, leader: 'brian', perf: 9.2, sales: 45},
              {id: 43, leader: 'steven', perf: 0.3},
              {id: 7, leader: 'joe', perf: 5.4}];
              
const pKey = 'id'; //the primary key used for joining
//index arrayB by the key for easier lookup
const mapB = toMap(pKey, arrayB);

for (const entryA of arrayA) { //iterate over A
  const {id} = entryA;
  // get the corresponding object in B (if any)
  // and merge the two. If nothing is found then the merge is a no-op 
  Object.assign(entryA, mapB.get(id));
}

console.log(arrayA);

In the case of arrayB being very big, the new Map(data.map(item => [item[indexBy], item])); would first create a very big array and then give it to the Map constructor to consume. This might be inefficient, and for such a situation you can use a generator function to lazily create the entries instead of making them all at once

function* toEntries(keyMapper, valueMapper, data) {
  for (const item of data)
    yield [keyMapper(item), valueMapper(item)]
}

const toMap = (indexBy, data) =>
  new Map(toEntries(item => item[indexBy], x => x, data));

/* helper */
function* toEntries(keyMapper, valueMapper, data) {
  for (const item of data)
    yield [keyMapper(item), valueMapper(item)]
}

const toMap = (indexBy, data) =>
  new Map(toEntries(item => item[indexBy], x => x, data));
/* /helpers */

let arrayA = [{id: 1, name: 'ghasem'},
              {id: 2, name: 'ahmad'},
              {id: 43, name: ',mahmoud'},
              {id: 5, name: 'ola'}];
let arrayB = [{id: 1, leader: 'brittney', perf: 4.3},
              {id: 2, leader: 'brian', perf: 9.2, sales: 45},
              {id: 43, leader: 'steven', perf: 0.3},
              {id: 7, leader: 'joe', perf: 5.4}];
              
const pKey = 'id'; //the primary key used for joining
//index arrayB by the key for easier lookup
const mapB = toMap(pKey, arrayB);

for (const entryA of arrayA) { //iterate over A
  const {id} = entryA;
  // get the corresponding object in B (if any)
  // and merge the two. If nothing is found then the merge is a no-op 
  Object.assign(entryA, mapB.get(id));
}

console.log(arrayA);

  • Related