1

Following are the 2 arrays I have,

 var g= [ 
        { id: 36, name: 'AAA', goal: 'yes' },
        { id: 40, name: 'BBB', goal: 'yes' },
        { id: 39, name: 'JJJ', goal: 'yes' },
        { id: 27, name: 'CCC', goal: 'yes' }];

    var c= [ 
        { id: 36, color:"purple" },
        { id: 40, color:"purple" },
        { id: 100, color:"pink"} ];

Desired output(left join on 'id'):

    res = [{ id: 36, name: 'AAA', goal: 'yes' , color:"purple"}, { id: 40, name: 'BBB', goal: 'yes', color:"purple" }]

here's an existing logic which does merger but I'm looking for a logic for left join:

    function mergeBy(key, data) {
      return Array.from(data
          .reduce((m, o) => m.set(o[key], { ...m.get(o[key]), ...o }), new Map)
          .values()
      );
    }
6
  • Are the arrays sorted according to id? Commented Apr 7, 2021 at 11:32
  • 2
    This isnt a "left join" at all. Your result implies just adding the properties from the second array where the object appears in the first - if anything thats closer to an inner join. Commented Apr 7, 2021 at 11:33
  • @Jamiec: as I told in the question, I have this merge logic but I don't know how to implement left join. Commented Apr 7, 2021 at 11:37
  • @SomShekharMukherjee: currently not but can be sorted Commented Apr 7, 2021 at 11:38
  • But you're not asking for a left join! You're asking for a merge of objects (something like an inner join). See if my answer is what you want Commented Apr 7, 2021 at 11:40

6 Answers 6

3

Here are some joins for you, take your pick!

function* innerJoin(a, b, key) {
    let idx = new Map(b.map(x => [key(x), x]));
    for (let x of a) {
        let k = key(x);
        if (idx.has(k))
            yield {...x, ...idx.get(k)};
    }
}

function* leftJoin(a, b, key) {
    let idx = new Map(b.map(x => [key(x), x]));
    for (let x of a) {
        let k = key(x);
        yield idx.has(k) ? {...x, ...idx.get(k)} : x;
    }
}

function* rightJoin(a, b, key) {
    let idx = new Map(a.map(x => [key(x), x]));
    for (let x of b) {
        let k = key(x);
        yield idx.has(k) ? {...idx.get(k), ...x} : x;
    }
}


//

var A = [
    {id: 1, a: 'a1'},
    {id: 2, a: 'a2'},
    {id: 7, a: 'a3'},
    {id: 8, a: 'a4'}
];

var B = [
    {id: 1, b: 'b1'},
    {id: 2, b: 'b2'},
    {id: 9, b: 'b3'}
];


console.log('INNER:')
console.log(...innerJoin(A, B, x => x.id))
console.log('LEFT')
console.log(...leftJoin(A, B, x => x.id))
console.log('RIGHT')
console.log(...rightJoin(A, B, x => x.id))

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

Comments

1

What your expected results asks for is not a left join. You're asking for an inner join of g and c using id and to merge the properties. The below should do this

var g= [ 
        { id: 36, name: 'AAA', goal: 'yes' },
        { id: 40, name: 'BBB', goal: 'yes' },
        { id: 39, name: 'JJJ', goal: 'yes' },
        { id: 27, name: 'CCC', goal: 'yes' }];

var c= [ 
        { id: 36, color:"purple" },
        { id: 40, color:"purple" },
        { id: 100, color:"pink"} ];
    
function mergeBy(key, dataL, dataR) {
  const rMap = dataR.reduce((m, o) => m.set(o[key], { ...m.get(o[key]), ...o }), new Map);
  
  return dataL.filter(x => rMap.get(x[key])).map(x => ({...x, ...rMap.get(x[key]) }));
}

console.log(mergeBy("id",g, c))

1 Comment

This code works great, how to update this code if the key names are different? for instance: the left key is id1 and right key is id2 and join these arrays
1

You could get uncommon keys and filter the merged results with.

const
    mergeCommon = (a, b, key) => {
        const aByKey = a.reduce((m, o) => m.set(o[key], o), new Map);

        return b.reduce((r, o) => {
            if (aByKey.has(o[key])) r.push({ ... aByKey.get(o[key]), ...o});
            return r;
        }, []);
    },
    g = [{ id: 36, name: 'AAA', goal: 'yes' , 'random': 27 }, { id: 40, name: 'BBB', goal: 'yes' }, { id: 39, name: 'JJJ', goal: 'yes' }, { id: 27, name: 'CCC', goal: 'yes' , lag: "23.3343" }],
    c = [{ id: 36, name: 'AAA', goal: 'yes', color:"purple" }, { id: 40, name: 'BBB', circle: 'yes', color:"purple" }, { id: 100, name: 'JJJ', circle: 'yes'}],
    result = mergeCommon(g, c, 'id');

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

2 Comments

Is that even a Left Join? Shouldn't Left Join have all items from the first array.
@SomShekharMukherjee, actually it's an inner join.
1

You can use Maps for joining two arrays. See the code snippet for three types of joins Left Join, Right Join, Inner Join.

All the three joins take O(N) time.

const g = [
  { id: 36, name: 'AAA', goal: 'yes', random: 27 },
  { id: 40, name: 'BBB', goal: 'yes' },
  { id: 39, name: 'JJJ', goal: 'yes' },
  { id: 27, name: 'CCC', goal: 'yes', lag: '23.3343' },
];

const c = [
  { id: 36, name: 'AAA', goal: 'yes', color: 'purple' },
  { id: 40, name: 'BBB', circle: 'yes', color: 'purple' },
  { id: 100, name: 'JJJ', circle: 'yes' },
];

const gMap = new Map(g.map(o => [o.id, o]));
const cMap = new Map(c.map(o => [o.id, o]));

const leftJoin = g.reduce(
  (a, o) => (cMap.has(o.id) ? [...a, { ...o, ...cMap.get(o.id) }] : [...a, o]),
  []
);

const rightJoin = c.reduce(
  (a, o) => (gMap.has(o.id) ? [...a, { ...o, ...gMap.get(o.id) }] : [...a, o]),
  []
);

const innerJoin = g.reduce(
  (a, o) => (cMap.has(o.id) ? [...a, { ...o, ...cMap.get(o.id) }] : a),
  []
);

console.log("LEFT JOIN\n", leftJoin)
console.log("RIGHT JOIN\n", rightJoin)
console.log("INNER JOIN\n", innerJoin)

Comments

0

var g = [ 
    { id: 36, name: 'AAA', goal: 'yes' , 'random':27},
    { id: 40, name: 'BBB', goal: 'yes' },
    { id: 39, name: 'JJJ', goal: 'yes' },
    { id: 27, name: 'CCC', goal: 'yes' , lag: "23.3343"}
];

var c = [ 
    { id: 36, name: 'AAA', goal: 'yes', color:"purple" },
    { id: 40, name: 'BBB', circle: 'yes', color:"purple" },
    { id: 100, name: 'JJJ', circle: 'yes'} 
];


const combinedArr = (arr1, arr2) => {
//Filter the arr2 and find the only matching elements from the first array
    const filteredArr = arr2.filter(({id}) => arr1.some(({id: arr1Id}) => arr1Id === id));
    //Loop through the filtered array and fetch the matching item from first and add obj from filtered array
    return filteredArr.map(obj => {
        return {
            ...arr1.find(({id}) => id === obj.id),
            ...obj
        }
    })
}    
console.log(combinedArr(g, c));
.as-console-wrapper { 
  max-height: 100% !important;
}

Comments

0

There you go, one liner

 var g= [ 
        { id: 36, name: 'AAA', goal: 'yes' },
        { id: 40, name: 'BBB', goal: 'yes' },
        { id: 39, name: 'JJJ', goal: 'yes' },
        { id: 27, name: 'CCC', goal: 'yes' }];

    var c= [ 
        { id: 36, color:"purple" },
        { id: 40, color:"purple" },
        { id: 100, color:"pink"} ];



const result = g.map(i => !!c.find(e => e.id === i.id) && ({...i, ...c.find(e => e.id === i.id)})).filter(Boolean);
console.log(result);

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.