0

I need to implement a javascript fx to merge associative array that may have a non defined structure.

I mean that sometimes i need to merge

[
    { name: 'Stefano', age:46 },
    { name: 'Name2', age:48 }
]
with
[
    { name: 'Stefano', location:'Italy' },
    { name: 'Name3', Location:'Elsewhere' }
]

and the goal is to obtain an array like this:

[
    { name: 'Stefano', age:46, location:'Italy' },
    { name: 'Name2', age:48 },
    { name: 'Name3', location:'Elsewhere' }
]

as long as to merge array with a different structure

 [
   { x:1, y:2, type:'a', category:8 },
   { x:1, y:3, type:'a', category:9 }
]
with
[
   { x:1, y:2, bin:23, site:34 },
   { x:1, y:3, bin:56, site:78 }
]

to obtain an array like this:

 [
     { x:1, y:2, type:'a', category:8, bin:23, site:34 },
     { x:1, y:3, type:'a', category:9, bin:56, site:78 }
 ]

The array are taken from external CSV conversion and the labels/keys are not always the same (they may change accordingly by the user).

Is there a smart way to:

  1. Detect keys in common with 2 associative array
  2. Combine their values
  3. Then merge records with common values?
2
  • 1
    Just to clerify, is it two arrays with one element in each one [{ name: 'Stefano', age:46 }] [{ name: 'Name2', age:48 }] but not one array with two elements? [{ name: 'Stefano', age:46 }, { name: 'Name2', age:48 }] Commented Jun 9, 2023 at 8:51
  • What about if you have two "Stephano" with different first names [{firstname:"Massini", lastname:"Stefano", age:48}] and in another Array [{firstname:"Parmigiano", lastname:"Stefano", age:48}] ??? How would you select the common fields? Commented Jun 9, 2023 at 9:54

2 Answers 2

2

This following approach should do the trick. It basically iterate through all the objects and find their common keys, and then group the items based on the same value of the common keys.

It also supports combining multiple arrays at the same time such as combine(arr1, arr2, arr3) etc.

const combine = (...arrs) => {
    const commonKeys = arrs.flat().reduce((acc, cur) => {
        if (!acc.length) {
            return Object.keys(cur);
        } else {
            return acc.filter(key => Object.keys(cur).includes(key));
        }
    }, []);

    return arrs.reduce((acc, cur) => {
        cur.forEach(obj => {
            const target = acc.find(entry => commonKeys.every(key => entry[key] === obj[key]));

            if (target) {
                Object.assign(target, obj);
            } else {
                acc.push({ ...obj });
            }
        });

        return acc;
    }, []);
};

const arr1 = [
    { name: 'Stefano', age: 46 },
    { name: 'Name2', age: 48 },
];

const arr2 = [
    { name: 'Stefano', location: 'Italy' },
    { name: 'Name3', Location: 'Elsewhere' },
];

const result = combine(arr1, arr2);
console.log(result);

It also works for your second test case which contains multiple common keys:

const combine = (...arrs) => {
    const commonKeys = arrs.flat().reduce((acc, cur) => {
        if (!acc.length) {
            return Object.keys(cur);
        } else {
            return acc.filter(key => Object.keys(cur).includes(key));
        }
    }, []);

    return arrs.reduce((acc, cur) => {
        cur.forEach(obj => {
            const target = acc.find(entry => commonKeys.every(key => entry[key] === obj[key]));

            if (target) {
                Object.assign(target, obj);
            } else {
                acc.push({ ...obj });
            }
        });

        return acc;
    }, []);
};

const arr1 = [
    { x: 1, y: 2, type: 'a', category: 8 },
    { x: 1, y: 3, type: 'a', category: 9 },
];

const arr2 = [
    { x: 1, y: 2, bin: 23, site: 34 },
    { x: 1, y: 3, bin: 56, site: 78 },
];

const result = combine(arr1, arr2);
console.log(result);

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

Comments

1

It can be simplified a bit if using lodash or similar library is an option :

const a = [ { x:1, y:2, type:'a', category:8 }, { x:1, y:3, type:'a', category:9 } ]

const b = [ { x:1, y:2, bin:23  , site:34    }, { x:1, y:3, bin:56  , site:78    } ]

const all = a.concat(b)

const commonKeys = _.intersection( ...all.map(_.keys) )

const grouped = _.groupBy( all, o => commonKeys.map(k => o[k]) )

const result = _.values(grouped).map(a => _.merge(...a))

console.log( JSON.stringify(result) )
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>

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.