I have this code that supposed to merge 3 arrays based on a matching siteid
.
This works to an extend. Basically, it only finds 1 siteid and ignores the Same siteides!
This is the code:
if you run the code below, you see that it only adds one of the forms with siteid of 77 to the merged Array! but it should add all the forms with the same siteid (77 in this example) to the merged array.
var siteS1 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
}
];
var siteS2 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
}
];
var form = [{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 96,
}
];
const merge = (...arrayList) => {
const siteids = arrayList
.map(array => array.map(item => item.siteid))
.flat()
return Array.from(new Set(siteids))
}
const find = (siteid, array) => {
const found = array.find(item => item.siteid === siteid)
if (!found) return undefined // returns undefined if item was not found
const {
siteid: _,
...restFoundData
} = found // omit `siteid` from found item
return restFoundData
}
const populate = (siteids) => siteids.map(siteid => {
const foundSiteS1 = find(siteid, siteS1)
const foundSiteS2 = find(siteid, siteS2)
const foundForm = find(siteid, form)
return {
siteS1: foundSiteS1,
siteS2: foundSiteS2,
form: foundForm ? [foundForm.data] : [], // empty array if no items matched
}
})
const result = populate(merge(siteS1, siteS2, form))
console.log(result)
What am I missing here?
EDIT:
Here is the example code:
var siteS1 = [{
Date: '2021-02-02 11:19',
siteid: 77,
}];
var siteS2 = [{
Date: '2021-02-02 11:19',
siteid: 77,
}];
var form = [{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 78,
}];
The expected result is this:
[{
"siteS1": {
"Date": "2021-02-02 11:19"
},
"siteS2": {
"Date": "2021-02-02 11:19"
},
"form": [{},{},{},{}]
}]
But the current code's results is this:
[{
"siteS1": {
"Date": "2021-02-02 11:19"
},
"siteS2": {
"Date": "2021-02-02 11:19"
},
"form": [{}]
}]
as you can see, it only finds 1 of the forms with the siteid of 77 and ignores the rest.
CodePudding user response:
Array.find
finds first matching element only, I use another function to find all matching data for the form
const findAll = (siteid, array) => {
return array.filter(item => item.siteid === siteid).map(({siteid, ...data}) => data);
}
var siteS1 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
}
];
var siteS2 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
}
];
var form = [{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 96,
}
];
const merge = (...arrayList) => {
const siteids = arrayList
.map(array => array.map(item => item.siteid))
.flat()
return Array.from(new Set(siteids))
}
const find = (siteid, array) => {
const found = array.find(item => item.siteid === siteid)
if (!found) return undefined // returns undefined if item was not found
const {
siteid: _,
...restFoundData
} = found // omit `siteid` from found item
return restFoundData
}
const findAll = (siteid, array) => {
return array.filter(item => item.siteid === siteid).map(({siteid, ...data}) => data);
}
const populate = (siteids) => siteids.map(siteid => {
const foundSiteS1 = find(siteid, siteS1)
const foundSiteS2 = find(siteid, siteS2)
const foundForm = findAll(siteid, form)
return {
siteS1: foundSiteS1,
siteS2: foundSiteS2,
form: foundForm, // empty array if no items matched
}
})
const result = populate(merge(siteS1, siteS2, form))
console.log(result)
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
So there are a few things here...
First lets get the matching site ids from the two arrays:
const ids = [
...siteS1.map(({ siteid }) => siteid),
...siteS2.map(({ siteid }) => siteid),
].filter((x, i, arr) => arr.indexOf(x) !== i); // non-unique, i.e. in both arrays
Ok, now we need the entries from form
:
const results = ids.map(id => {
return {
siteS1: {
Date: siteS1.find(({ siteid }) => siteid === id).Date,
},
siteS2: {
Date: siteS2.find(({ siteid }) => siteid === id).Date,
}
form: form.filter(({ siteid }) => siteid === id),
};
});
That will give you your result for every site id in both arrays in an array.
CodePudding user response:
The easiest way to handle is is creating maps by id so you can easily look up data. This is so you are not constantly looping over arrays trying to find all of the things that match.
var siteS1 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
}
];
var siteS2 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
}
];
var form = [{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 96,
}
];
const keyBySiteIdDate = (sites) => sites.reduce((acc, site) => {
acc[site.siteid] = site.Date;
return acc;
}, {});
const keyBySiteIdData = (sites) => sites.reduce((acc, site) => {
acc[site.siteid] = acc[site.siteid] || [];
acc[site.siteid].push(site.data);
return acc;
}, {});
const sitesKeyed1 = keyBySiteIdDate(siteS1);
const sitesKeyed2 = keyBySiteIdDate(siteS2);
const dataKeyed = keyBySiteIdData(form);
// as an array
const result = Object.entries(sitesKeyed1).map(([siteId, date]) => {
if (sitesKeyed2[siteId]) {
return {
siteId,
siteS1: date,
siteS2: sitesKeyed2[siteId],
form: dataKeyed[siteId] || [],
}
}
return null;
}).filter(Boolean);
console.log(result);
// as an object
const result2 = Object.entries(sitesKeyed1).reduce((acc, [siteId, date]) => {
if (sitesKeyed2[siteId]) {
acc[siteId] = {
siteS1: date,
siteS2: sitesKeyed2[siteId],
form: dataKeyed[siteId] || [],
}
}
return acc;
}, {});
console.log(result2);
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
This assumes the values are in both arrays. If they can be in one or the other and you need that data, it is a bit more looping, but it is doable.
var siteS1 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
}
];
var siteS2 = [{
Date: '2021-02-02 11:19',
siteid: 77,
},
{
Date: '2021-02-02 11:19',
siteid: 76,
},
{
Date: '2021-02-02 11:19',
siteid: 66,
},
{
Date: '2021-02-02 11:19',
siteid: 96,
},
{
Date: '2021-02-02 11:19',
siteid: 11111111111,
}
];
var form = [{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 77,
},
{
data: {},
siteid: 96,
}
];
const keyBySiteIdDate = (sites) => sites.reduce((acc, site) => {
acc[site.siteid] = site.Date;
return acc;
}, {});
const keyBySiteIdData = (sites) => sites.reduce((acc, site) => {
acc[site.siteid] = acc[site.siteid] || [];
acc[site.siteid].push(site.data);
return acc;
}, {});
const sitesKeyed1 = keyBySiteIdDate(siteS1);
const sitesKeyed2 = keyBySiteIdDate(siteS2);
const dataKeyed = keyBySiteIdData(form);
const uniqueSiteIds = Array.from(new Set([...Object.keys(sitesKeyed1).concat(Object.keys(sitesKeyed2))]).values())
const result = uniqueSiteIds.map(siteId => ({
siteId,
siteS1: sitesKeyed1[siteId],
siteS2: sitesKeyed2[siteId],
form: dataKeyed[siteId] || [],
}));
console.log(result);
// as an object
const result2 = uniqueSiteIds.reduce((acc, siteId) => {
acc[siteId] = {
siteS1: sitesKeyed1[siteId],
siteS2: sitesKeyed2[siteId],
form: dataKeyed[siteId] || [],
};
return acc;
}, {});
console.log(result2);
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
To merge two arrays, normally you want to have them sorted first. Once sorted, the standard algorithm gives you O(n m) complexity.
Just check https://www.geeksforgeeks.org/merge-two-sorted-arrays/ they have done good job explaining it.
CodePudding user response:
That's probably not the best solution but it's the first thing i thought
[...new Set([...siteS1, ...siteS2, ...forms].map(s => s.siteid))].map(id => ({
siteS1: siteS1.find(s => s.siteid === id);
siteS2: siteS2.find(s => s.siteid === id);
form: form.filter(f => f.siteid === id);
}));