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
.
- Index
arrayB
by theid
property for easier lookup:O(m)
operation. - Iterate over
arrayA
(O(n)
) and look items up from the map (O(1)
) then merge the two usingObject.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);