0

I have two arrays of objects:

A = [
    { id1: "1", id2: "a", p1: "3", p2: "4" },
    { id1: "1", id2: "b", p1: "5", p2: "6" },
    { id1: "2", id2: "a", p1: "7", p2: "8" },
    { id1: "2", id2: "b", p1: "9", p2: "10" }
];


B = [
    { id1: "1", id2: "a", p3: "13", p4: "14" },
    { id1: "1", id2: "b", p3: "15", p4: "16" },
    { id1: "2", id2: "a", p3: "17", p4: "18" },
    { id1: "2", id2: "b", p3: "19", p4: "20" }
];

I need a function that makes an inner join between the two objects based on the two properties id1 and id2, making a union of the other properties (properties names are never equal except for id1 and id2) In other words I need as a result:

C = [
    { id1: "1", id2: "a", p1: "3", p2: "4", p3: "13", p4: "14" },
    { id1: "1", id2: "b", p1: "5", p2: "6", p3: "15", p4: "16" },
    { id1: "2", id2: "a", p1: "7", p2: "8", p3: "17", p4: "18" },
    { id1: "2", id2: "b", p1: "9", p2: "10", p3: "19", p4: "20" }
];

In here I can find a way to make the join using one single key. I need an extention for the multiple keys case.

http://learnjsdata.com/combine_data.html

3
  • What part of extending this code are you having trouble with? Commented Apr 24, 2019 at 14:27
  • 1
    Could you make sure you Object literals are valid,.. It helps others help you. Commented Apr 24, 2019 at 14:28
  • Amended. Sotry about that. The provided code works fine for a single key only. I need to extend it for the mutliple keys case Commented Apr 24, 2019 at 14:32

3 Answers 3

2

Both provided answers DO NOT deliver expected result (inner join which is supposed to contain only matching items from both data sets).

I have somewhat extended input arrays in order to demonstrate the difference of output for provided answers and mine:

const A = [
	{id1:"1", id2:"a", p1:"3", p2:"4"},
	{id1:"1", id2:"b", p1:"5", p2:"6"},
	{id1:"2", id2:"a", p1:"7", p2:"8"},
	{id1:"2", id2:"b", p1:"9", p2:"10"},
	{id1:"3", id2:"c", p1:"1", p2:"3"}
];

const B = [
	{id1:"1", id2:"a", p3:"13", p4:"14"},
	{id1:"1", id2:"b", p3:"15", p4:"16"},
	{id1:"2", id2:"a", p3:"17", p4:"18"},
	{id1:"2", id2:"b", p3:"19", p4:"20"},
	{id1:"4", id2:"k", p3:"11", p4:"13"}
];

const innerJoined = A.reduce((result, itemA) => {
  const itemB = B.find(itemB => itemB.id1 == itemA.id1 && itemB.id2 == itemA.id2);
  if(itemB) result.push({...itemA, ...itemB});
  return result;
}, []);

console.log(innerJoined);

Sign up to request clarification or add additional context in comments.

12 Comments

You're quite welcome. I must admit I didn't put enough effort into my answer yesterday as I was in a hurry to get out of the office. Today I've fixed my code, it is way more neat now and works noticeably faster, than accepted answer.
noticeably faster That was an interesting result, in theory using a object hash lookup should give better results. But I've a feeling it's the creating of the lookup key that's slowing this down. Array.find has a time complexity of O(n), but it looks like Javascript is so fast nowadays it's hardly a thing.. :) ps, I've made your code into a snippet, it's just nicer. Instead of clicking {} icon, click the <> instead.
My guess is that my code is faster due to usage of some() and find() as both are stopping to iterate over target array upon first match. Thanks for turning my code into executable snippet, I've edited my post from mobile, couldn't use that feature
@user11299053 How would you modifiy your code to have a outer join instead?
@MauroGentile: I guess, that would be something, like that
|
2

You can make a lookup based on one array with a composite key from the two ids, then loop through the second, look up the merged key, and create a new object from the two items using Object.assign(). This will allow you to make the list without having an O(n²) solution of searching A for every item in B

let A=[
    {id1:"1", id2:"a", p1:"3", p2:"4"},
    {id1:"1", id2:"b", p1:"5", p2:"6"},
    {id1:"2", id2:"a", p1:"7", p2:"8"},
    {id1:"2", id2:"b", p1:"9", p2:"10"}
]

let B=[{id1:"1", id2:"a", p3:"13", p4:"14"},
       {id1:"1", id2:"b", p3:"15", p4:"16"},
       {id1:"2", id2:"a", p3:"17", p4:"18"},
       {id1:"2", id2:"b", p3:"19", p4:"20"}
]
  
let lookup = A.reduce((obj, item) => {
  obj[`${item.id1}_${item.id2}`] = item // key in form of id1_id2
  return obj
}, {})

let merged = B.reduce((arr, item) => {
  if (lookup[`${item.id1}_${item.id2}`]) {
    arr.push(Object.assign({}, lookup[`${item.id1}_${item.id2}`], item))
  }
  return arr
}, [])

console.log(merged)

You just need to make sure the separator of the key (_ here) doesn't exist in you ids. If that's a problem, you could make a nested object with lookups like obj[id1][id2]:

let lookup = A.reduce((obj, item) => {
    if (!obj[item.id1]) obj[item.id1] = {}
    obj[item.id1][item.id2] = item
    return obj
}, {} )

and adjust the reduce() callback to fit.

Comments

2

If the array's are very large, using a lookup like @MarkMeyer shows would might be better option. But for smaller arrays doing a simple map, array spread & find works fine.

As pointed out by @user11299053 this is not the equivalent of an inner join, maybe a left join instead,.. So I'll leave this as is, it might be useful. :)

const A=[
  {id1:"1", id2:"a", p1:"3", p2:"4"},
  {id1:"1", id2:"b", p1:"5", p2:"6"},
  {id1:"2", id2:"a", p1:"7", p2:"8"},
  {id1:"2", id2:"b", p1:"9", p2:"10"}
];

const B=[
  {id1:"1", id2:"a", p3:"13", p4:"14"},
  {id1:"1", id2:"b", p3:"15", p4:"16"},
  {id1:"2", id2:"a", p3:"17", p4:"18"},
  {id1:"2", id2:"b", p3:"19", p4:"20"}
];
  
const merged = B.map(item => ({
  ...A.find(
    f => f.id1 === item.id1 && f.id2 === item.id2),
  ...item
}));

console.log(merged);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.