Suppose i have an array of objects like this
let arr = [
{
abcdef: {name: 'Robin', uid: '123'},
ghijkl: {name: 'Simon', uid: '456'}
},
{
mnopqr: {name: 'Alex', uid: '789'},
abcdef: {name: 'Robin', uid: '123'}
},
{
abcdef: {name: 'Robin', uid: '123'},
stuvwx: {name: 'Julianna', uid: '111'},
yzxwuv: {name: 'Elon', uid: '007'}
}
];
In position of arr[0]
, arr[1]
and arr[2]
, i define a object and inside that object, i define couple of objects.
Here this abcdef: {name: 'Robin', uid: '123'}
is common among the three(arr[0], arr[1], arr[2]). So i need to write a function that returns the common one. In this case abcdef: {name: 'Robin', uid: '123'}
UPDATE: If there is nothing in common, return false. And two or more in common, return all of them.
You can easily do that by using intersectionWith
- it accepts a custom comparator for your elements. The easiest way is to convert the object into an array of entries using toPairs
and then compare those with isEqual
. The intersection would then be an array containing pairs of attribute and value, so you can then convert it back to an object using fromPairs
let arr = [ { abcdef: {name: 'Robin', uid: '123'}, ghijkl: {name: 'Simon', uid: '456'} }, { mnopqr: {name: 'Alex', uid: '789'}, abcdef: {name: 'Robin', uid: '123'} }, { abcdef: {name: 'Robin', uid: '123'}, stuvwx: {name: 'Julianna', uid: '111'}, yzxwuv: {name: 'Elon', uid: '007'} } ]; const inputPairs = arr.map(_.toPairs); const resultPairs = _.intersectionWith(...inputPairs, _.isEqual); const resultObject = _.fromPairs(resultPairs); console.log(resultObject);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>
Using chaining this can be written as:
let arr = [ { abcdef: {name: 'Robin', uid: '123'}, ghijkl: {name: 'Simon', uid: '456'} }, { mnopqr: {name: 'Alex', uid: '789'}, abcdef: {name: 'Robin', uid: '123'} }, { abcdef: {name: 'Robin', uid: '123'}, stuvwx: {name: 'Julianna', uid: '111'}, yzxwuv: {name: 'Elon', uid: '007'} } ]; const resultObject = _(arr) .map(_.toPairs) .thru(pairs => _.intersectionWith(...pairs, _.isEqual)) .fromPairs() .value(); console.log(resultObject);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>
Using thru
here because the chain contains an array with other array in it and each needs to be passed as a separate argument to intersectionWith
. It's the easiest way to do that.
Alternatively, if you prefer a more FP approach then it can look like this:
const {spread, intersectionWith, isEqual, flow, map, toPairs, fromPairs} = _; let arr = [ { abcdef: {name: 'Robin', uid: '123'}, ghijkl: {name: 'Simon', uid: '456'} }, { mnopqr: {name: 'Alex', uid: '789'}, abcdef: {name: 'Robin', uid: '123'} }, { abcdef: {name: 'Robin', uid: '123'}, stuvwx: {name: 'Julianna', uid: '111'}, yzxwuv: {name: 'Elon', uid: '007'} } ]; const process = flow( map(toPairs), spread(intersectionWith(isEqual)), fromPairs ); const resultObject = process(arr); console.log(resultObject);
<script src="https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)"></script>
Solution
Here's a quick solution I was able to whip up, and I am doing this without lodash
intentionally just to illustrate how to do it natively
const arr = [ { abcdef: { name: 'Robin', uid: '123' }, ghijkl: { name: 'Simon', uid: '456' }, }, { mnopqr: { name: 'Alex', uid: '789' }, abcdef: { name: 'Robin', uid: '123' }, }, { abcdef: { name: 'Robin', uid: '123' }, stuvwx: { name: 'Julianna', uid: '111' }, yzxwuv: { name: 'Elon', uid: '007' }, }, ]; function isEqual(a, b) { const aProps = Object.getOwnPropertyNames(a); const bProps = Object.getOwnPropertyNames(b); if (aProps.length !== bProps.length) { return false; } for (const prop of aProps) { if (a[prop] !== b[prop]) { return false; } } return true; } function findIntersection() { const results = []; const objCount = {}; arr.forEach(element => { Object.entries(element).forEach(entry => { const [key, value] = entry; if (!objCount[key]) { objCount[key] = {}; objCount[key].value = value; objCount[key].count = 1; } else if (isEqual(value, objCount[key].value)) { objCount[key].count += 1; } }); }); Object.keys(objCount).forEach(key => { if (objCount[key].count === arr.length) { results.push({ [key]: objCount[key].value }); } }); if (results.length > 0) { return results; } return false; } const results = findIntersection(arr); console.log('Results', results);
Explanation
The logic is as follows,
Hope this helps. Let me know if it works for you.
Just for your reference:
let arr = [
{
abcdef: {name: 'Robin', uid: '123'},
ghijkl: {name: 'Simon', uid: '456'}
},
{
mnopqr: {name: 'Alex', uid: '789'},
abcdef: {name: 'Robin', uid: '123'}
},
{
abcdef: {name: 'Robin', uid: '123'},
stuvwx: {name: 'Julianna', uid: '111'},
yzxwuv: {name: 'Elon', uid: '007'}
}
];
let counterMap = {},
names=[], //names collect the keys only which exists in all item, here the value should be ['abcdef']
commons=[];
for (let itm of arr) {
for (let key of Object.keys(itm)) {
if (!counterMap[key]) {
counterMap[key] = 1;
} else {
counterMap[key] = counterMap[key] + 1;
}
}
}
_.forOwn(counterMap, (val, key) => {
if (val === arr.length) {
names.push(key);
}
})
names.forEach(name => {
if (_.every(arr, itm => {
return _.isEqual(itm[name], arr[0][name]);
})){
let result = {};
result[name] = arr[0][name];
commons.push(result)
}
})
console.log(commons);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.