nachocab
nachocab

Reputation: 14374

How can I left join two javascript objects of arrays using properties as primary keys?

I have two objects that represent two SQL tables. Here are two simplified versions (they both might have many more properties):

const object1 = {
  key1: [  1,   1,   1,   2,   2,  3 ],
  key2: ['a', 'b', 'c', 'a', 'c', 'b'],
  prop3: ['A', 'B', 'C', 'D', 'E', 'F'],
}
const object2 = {
  key1: [  1,   1,   2],
  key2: ['a', 'c', 'a'],
  prop4: [10, 20, 30],
}

I would like to perform a left-join on key1 and key2. Something like:

select * 
from object1 o1
left join object2 o2 on o1.key1 = o2.key1 and o1.key2 = o2.key2

Or in JS:

const object12 = {
  key1: [  1,   1,   1,   2,   2,  3 ],
  key2: ['a', 'b', 'c', 'a', 'c', 'b'],
  prop3: ['A', 'B', 'C', 'D', 'E', 'F'],
  prop4: [10, null, 20, 30, null, null],
}

What's a convenient way to do this in JS? (using lodash is allowed)

Upvotes: 0

Views: 82

Answers (1)

Jonas Wilms
Jonas Wilms

Reputation: 138277

const table = (entries, keys) => {
  const toData = () => Object.fromEntries(keys.map(k => [k, entries.map(it => it[k] ?? null)]));
  const join = (other, on) => table(
    entries.map(entry => ({ ...entry, ...(other.entries.find(other => on(entry, other)) ?? {}) })),
    [...keys, ...other.keys]
  );
  return { entries, keys, join, toData };
};

table.from = data => {
  const keys = Object.keys(data);
  const entries = [];  

  for(let i = 0; i < data[keys[0]].length; i++) {
    const entry = entries[i] = {};
    for(const key of keys)
      entry[key] = data[key][i];
  }

  return table(entries, keys);
};

In action:

  const table = (entries, keys) => {
     const toData = () => Object.fromEntries(keys.map(k => [k, entries.map(it => it[k] ?? null)]));
     const join = (other, on) => table(
        entries.map(entry => ({ ...entry, ...(other.entries.find(other => on(entry, other)) ?? {}) })),
        [...keys, ...other.keys]
     );
     return { entries, keys, join, toData };
  };

  table.from = data => {
      const keys = Object.keys(data);
      const entries = [];  

      for(let i = 0; i < data[keys[0]].length; i++) {
        const entry = entries[i] = {};
        for(const key of keys)
          entry[key] = data[key][i];
      }

     return table(entries, keys);
 };

 const object1 = { key1: [  1,   1,   1,   2,   2,  3 ], key2: ['a', 'b', 'c', 'a', 'c', 'b'], prop3: ['A', 'B', 'C', 'D', 'E', 'F'] };
 const object2 = { key1: [  1,   1,   2], key2: ['a', 'c', 'a'], prop4: [10, 20, 30] };

 const result = table.from(object1)
   .join(table.from(object2), (a, b) => a.key1 === b.key1 && a.key2 === b.key2)
   .toData();

 console.log(result);

Upvotes: 1

Related Questions