0

I have two sets of data that need to be combined and grouped a certain way.

The first set of data:

let overallCert =
[
  {
      "id": "1",
      "entity_id": "3",
      "status": "Certified",
  },
  {
      "id": "379",
      "entity_id": "417",
      "status": "Certified",
  }
];

The second set of data:

let userCerts =
[
  {
      "id": "640",
      "entity_id": "417",
      "method": "Field Study",
      "strand": "",
      "date_completed": "2016-07-15T08:00:00.000Z"
  },
  {
      "id": "814",
      "entity_id": "417",
      "method": "Field Study",
      "date_completed": "2019-07-15T08:00:00.000Z"
  },
  {
      "id": "844",
      "entity_id": "3",
      "method": "Online",
      "date_completed": "2022-03-28T08:00:00.000Z"
  },
  {
      "id": "845",
      "entity_id": "3",
      "method": "Field Study",
      "date_completed": "2022-03-28T08:00:00.000Z"
  }
];

I want to merge and group these arrays of objects by entity_id to create this output below...

Desired output:

let desiredOutput = 
[
  [
    [
      {
        "id": "640",
        "entity_id": "417",
        "method": "Field Study",
        "date_completed": "2016-07-15T08:00:00.000Z"
      },
      {
        "id": "814",
        "entity_id": "417",
        "method": "Field Study",
        "date_completed": "2019-07-15T08:00:00.000Z"
      },
    ],
    [
      {
        "id": "379",
        "entity_id": "417",
        "status": "Certified",
      }
    ]
  ],
  [
    [
      {
        "id": "844",
        "entity_id": "3",
        "method": "Online",
        "date_completed": "2022-03-28T08:00:00.000Z"
      },
      {
        "id": "845",
        "entity_id": "3",
        "method": "Field Study",
        "date_completed": "2022-03-28T08:00:00.000Z"
      }
    ],
    [
      {
        "id": "379",
        "entity_id": "417",
        "status": "Certified",
      }
    ]
  ]
];

So far, I have managed this:

let certsDataCombined = overallCert.map(item => ({ ...item,
  0 : userCerts.filter(c => c.entity_id == item.entity_id)
}));

let userCertsGroupBy = groupBy(certsDataCombined, "entity_id");

function groupBy(arr, prop) {
  const map = new Map(Array.from(arr, obj => [obj[prop], []]));
  arr.forEach(obj => map.get(obj[prop]).push(obj));
  return Array.from(map.values());
}

This code almost does the trick, but I need to encapsulate the overallCert data in its own array, plus the nesting is a little off. Here is the current output:

3
  • Can you tell me why the object with entity_id 417 { "id": "379", "entity_id": "417", "status": "Certified" } is there in second array in desired output? Also why the element with "id": "1" is not there in the desired output? Commented Mar 31, 2022 at 4:29
  • What should the overall ordering of the result be? Does it matter? Commented Mar 31, 2022 at 4:29
  • @Nitheesh That was a copy and paste mistake when I was creating the array in VS Code. I'd update the question, but the edit feature is not working for me right now Commented Mar 31, 2022 at 4:43

1 Answer 1

1

Seems you could build the output by mapping each overallCert to a new array and then pulling the matching userCerts using a filter

const overallCert = [{"id":"1","entity_id":"3","status":"Certified"},{"id":"379","entity_id":"417","status":"Certified"}];
const userCerts = [{"id":"640","entity_id":"417","method":"Field Study","strand":"","date_completed":"2016-07-15T08:00:00.000Z"},{"id":"814","entity_id":"417","method":"Field Study","date_completed":"2019-07-15T08:00:00.000Z"},{"id":"844","entity_id":"3","method":"Online","date_completed":"2022-03-28T08:00:00.000Z"},{"id":"845","entity_id":"3","method":"Field Study","date_completed":"2022-03-28T08:00:00.000Z"}];

const result = overallCert.map(cert => [
  userCerts.filter(({ entity_id }) => cert.entity_id === entity_id),
  [ cert ]
]);

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

Note the order is based on overallCert and not userCerts like in your question.


The above is O(n^2) time complexity which isn't fantastic. You could improve this by first grouping both arrays by entity_id for O(n). This would also let you order by userCerts like in your examples.

const overallCert = [{"id":"1","entity_id":"3","status":"Certified"},{"id":"379","entity_id":"417","status":"Certified"}];
const userCerts = [{"id":"640","entity_id":"417","method":"Field Study","strand":"","date_completed":"2016-07-15T08:00:00.000Z"},{"id":"814","entity_id":"417","method":"Field Study","date_completed":"2019-07-15T08:00:00.000Z"},{"id":"844","entity_id":"3","method":"Online","date_completed":"2022-03-28T08:00:00.000Z"},{"id":"845","entity_id":"3","method":"Field Study","date_completed":"2022-03-28T08:00:00.000Z"}];

// Helper function since Map is missing this
Map.prototype.getOrDefault = function(key, defaultValue) {
  return (this.has(key) ? this : this.set(key, defaultValue)).get(key);
};

// Groups certs by entity_id
const reducer = (map, cert) => (
  map.getOrDefault(cert.entity_id, []).push(cert),
  map
);

const overallCertsByEntityId = overallCert.reduce(reducer, new Map());
const userCertByEntityId = userCerts.reduce(reducer, new Map());

const result = Array.from(userCertByEntityId, ([key, certs]) => [
  certs,
  overallCertsByEntityId.get(key)
]);

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

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

1 Comment

This is exactly what I was looking for (order didn't matter, it's going into a db via an api). Thanks!

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.