Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging multiple arrays in javascript?

Tags:

javascript

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.

like image 852
drago Avatar asked Nov 04 '21 13:11

drago


5 Answers

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)
like image 99
ProGu Avatar answered Nov 02 '22 12:11

ProGu


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.

like image 31
Jared Smith Avatar answered Nov 02 '22 11:11

Jared Smith


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);

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);
like image 34
epascarello Avatar answered Nov 02 '22 13:11

epascarello


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.

like image 35
Deian Avatar answered Nov 02 '22 12:11

Deian


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);
}));
like image 23
Nick Avatar answered Nov 02 '22 12:11

Nick