如何改进javascript中的左联接语法



因此,我有以下函数,它使用两个对象数组中的主键pKeys执行等价的右联接。

这个功能运行得很好,但我想知道是否有办法提高它的性能,或者是否有更好的方法来实现这一点。

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

左、右、内和全联接的实现,这将在线性复杂性中工作,并通过使用迭代器和生成器来延迟提供值来避免占用太多内存:

/* helpers */
const get = prop => item =>
item[prop];
function* concat(...iterables) {
for (const iterable of iterables)
yield* iterable;
}
function* map(mapper, data) {
for (let item of data)
yield mapper(item);
}
function* filter(predicate, data) {
for (let item of data)
if (predicate(item))
yield item;
}
function* toEntries(keyMapper, valueMapper, data) {
for (const item of data)
yield [keyMapper(item), valueMapper(item)];
}
const toMap = (indexBy, data) =>
new Map(toEntries(get(indexBy), x => x, data));
/* /helpers */
//JOIN implementation
/**
* Function that combines two lists attempting to merge them when they overlap
* @param {Iterable} keysToCombine - iterable with the keys to pickk
* @param {string} pKey - key the lists to use for finding overlaps
* @param {iterable} - objects to combine
* @param {iterable} - objects to combine
* @return {array} - items from botth where a and b where pKey matching keysToCombine. 
* If an item exist in both places, it is merged into one object
*/
function combine(keysToCombine, pKey, a, b) {
const mapA = toMap(pKey, a);
const mapB = toMap(pKey, b);

return Array.from(keysToCombine, key => 
Object.assign({}, mapA.get(key), mapB.get(key))
);
}
const leftJoin = (on, left, right) => {
const ids = map(get(on), left);
return combine(ids, on, left, right);
}
const rightJoin = (on, left, right) => {
return leftJoin(on, right, left);
}

const innerJoin = (on, left, right) => {
const leftIds = new Set(map(get(on), left));
const intersectedIds = new Set(
filter(
x => leftIds.has(x),
map(
get(on),
right
)
)
);

return combine(intersectedIds, on, left, right);
}
const fullJoin = (on, left, right) => {
const unionIds = new Set(
concat(
map(get(on), left), 
map(get(on), right)
)
);

return combine(unionIds, on, left, right);
}
/// Usage
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 leftJoinResult  = leftJoin ("id", arrayA, arrayB);
const rightJoinResult = rightJoin("id", arrayA, arrayB);
const innerJoinResult = innerJoin("id", arrayA, arrayB);
const fullJoinResult  = fullJoin ("id", arrayA, arrayB);
console.log("---left join result---");
console.log(leftJoinResult);
console.log("---right join result---");
console.log(rightJoinResult);
console.log("---inner join result---");
console.log(innerJoinResult);
console.log("---full join result---");
console.log(fullJoinResult);
.as-console-wrapper { max-height: 100% !important; }

加入实施

combine()

一个方便函数,它将根据联接策略组合结果。由于它接受密钥,我们可以根据需要的连接类型来预处理密钥,然后移交给combine()来处理结果。

leftJoin()

直截了当——从left开始的所有内容。如果right中存在任何内容,则它将被合并。不考虑right独有的项目。

rightJoin()

只是leftJoin()的翻转版本,因为逻辑完全相同。通过交换leftright,我们得到了正确的功能。

innerJoin()

来自leftright的所有密钥的交集。仅选择重叠的关键点。

fullJoin()

来自leftright的所有密钥都已消除重复。

帮助者

当数组非常大时,生成器用于避免使用类似data.map(item => item[indexBy]);的东西创建一个全新数组的开销。map()filter()concat()生成器通常与arr.map()arr.filter()arr.concat()执行相同的操作,但仅根据需要提供结果,而不是急切地构建全新的阵列。

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

toEntries()toMap()类似地延迟生成映射,而不需要为map构造函数将使用的数组保留初始内存。

我们可以探索更好的循环,但很明显,使用HashMap可以保存一个用于查找的嵌套循环,使用spread运算符可以保存另一个循环。

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

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";
function mergeDataUsingKeyProvided(id, dataSets) {
// resusable function to carry operations for different dataSets

return dataSets[0].map(each => {
return {
...each, ...(dataSets[1].find((item) =>  item[id] === each[id]))
}
})
}
arrayA =  mergeDataUsingKeyProvided(pKey, [arrayA, arrayB]);
console.log(arrayA);

最新更新