Sometimes when you want to manage your code efficiency you may need to optimize some Java implementations.
Jointure implementation in Typescript may be such a case.
Underneath you will find my proposal to implement a jointure in Typescript environment. This is a generic code and may be optimized in many way for your implementation needs.
class ArrayExtensions {
static innerJoin<TL, TR, TK, TJ>(lefts: TL[], rights: TR[], onLeftKey: (l: TL) => TK, onRightKey: (r: TR) => TK, joinBuilder: (l: TL, r: TR) => TJ): TJ[] {
const leftsWithKeys = lefts.map(l => [l, onLeftKey(l)] as [TL, TK]);
const rightsWithKeys = rights.map(r => [r, onRightKey(r)] as [TR, TK]);
const rightGroupsByKey = rightsWithKeys.reduce((acc, [r, key]) => {
if (!acc[key]) acc[key] = [];
acc[key].push(r);
return acc;
}, {} as Record<TK, TR[]>);
const result = leftsWithKeys.flatMap(([l, key]) => {
const joineds = rightGroupsByKey[key] || [];
return joineds.map(joined => joinBuilder(l, joined));
});
return result;
}
static leftJoin<TL, TR, TK, TJ>(lefts: TL[], rights: TR[], onLeftKey: (l: TL) => TK, onRightKey: (r: TR) => TK, joinBuilder: (l: TL, r: TR | null) => TJ, defaultRight?: TR): TJ[] {
const leftsWithKeys = lefts.map(l => [l, onLeftKey(l)] as [TL, TK]);
const rightsWithKeys = rights.map(r => [r, onRightKey(r)] as [TR, TK]);
const rightGroupsByKey = rightsWithKeys.reduce((acc, [r, key]) => {
if (!acc[key]) acc[key] = [];
acc[key]. push(r);
return acc;
}, {} as Record<TK, TR[]>);
const result = leftsWithKeys.flatMap(([l, key]) => {
const joineds = rightGroupsByKey[key] || (defaultRight ? [defaultRight] : []);
return joineds.map(joined => joinBuilder(l, joined));
});
return result;
}
static fullJoin<TL, TR, TK>(lefts: TL[], rights: TR[], leftKeyFunc: (l: TL) => TK, rightKeyFunc: (r: TR) => TK, defaultLeft?: TL, defaultRight?: TR): [TL | undefined, TR | undefined][] {
const leftJoinedToRight = this.leftJoin(lefts, rights, leftKeyFunc, rightKeyFunc, (l, r) => [l, r] as [TL, TR | undefined], defaultRight);
const joinedRightKeys = new Set<TK>(leftJoinedToRight.filter(m => m[1] !== undefined).map(m => rightKeyFunc(m[1]!)));
const defaultLeftAndMissingRights = rights
.map(r => [r, rightKeyFunc(r)] as [TR, TK])
.filter(([_, key]) => !joinedRightKeys.has(key))
.map(([r, _]) => [defaultLeft, r] as [TL | undefined, TR]);
const fullJointure = leftJoinedToRight.concat(defaultLeftAndMissingRights);
return fullJointure;
}
}
Underneath the related unit tests
import { ArrayExtensions } from './ArrayExtensions';
// adjust the import path as necessary
describe('ArrayExtensions', () => {
describe('innerJoin', () => {
it('should correctly perform an inner join on two arrays', () => {
const lefts = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
const rights = [{ employeeId: 1, department: "Engineering" }, { employeeId: 2, department: "Human Resources" }];
const result = ArrayExtensions.innerJoin(
lefts,
rights,
l => l.id,
r => r.employeeId,
(l, r) => ({ name: l.name, department: r.department })
);
expect(result).toEqual([
{ name: "Alice", department: "Engineering" },
{ name: "Bob", department: "Human Resources" }
]);
});
});
describe('leftJoin', () => {
it('should correctly perform a left join on two arrays', () => {
const lefts = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 3, name: "Charlie" }];
const rights = [{ employeeId: 1, department: "Engineering" }, { employeeId: 2, department: "Human Resources" }];
const result = ArrayExtensions.leftJoin(
lefts,
rights,
l => l.id,
r => r.employeeId,
(l, r) => ({ name: l.name, department: r ? r.department : "None" })
);
expect(result).toEqual([
{ name: "Alice", department: "Engineering" },
{ name: "Bob", department: "Human Resources" },
{ name: "Charlie", department: "None" }
]);
});
});
describe('fullJoin', () => {
it('should correctly perform a full join on two arrays', () => {
const lefts = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
const rights = [{ employeeId: 2, department: "Human Resources" }, { employeeId: 3, department: "Marketing" }];
const result = ArrayExtensions.fullJoin(
lefts,
rights,
l => l.id,
r => r.employeeId,
{ id: 0, name: "Unknown" },
{ employeeId: 0, department: "None" }
);
expect(result).toEqual([
[{ id: 1, name: "Alice" }, undefined],
[{ id: 2, name: "Bob" }, { employeeId: 2, department: "Human Resources" }],
[undefined, { employeeId: 3, department: "Marketing" }]
]);
});
});
});