Filter based on multiple unique values

Viewed 48

I'm trying to determine the best approach to the following.

I have two arrays, as per the below:

const a = [
{
  Employee: "00001",
  Seq: "01",
  ReportDate: "2021-03-10T00:00:00.000",
  ReportName: "test report",
},
{
  Employee: "00002",
  Seq: "01",
  ReportDate: "2020-03-10T00:00:00.000",
  ReportName: "test report 2",
},
];


const b = [
{
  Employee: "00001",
  EmplName: "William Apple",
  ReportDate: "2021-03-10T00:00:00.000",
  ReportName: "test report",
  Posted: "Y",
  Seq: "01",
},
{
  Employee: "00003",
  EmplName: "John Smith",
  ReportDate: "2021-03-10T00:00:00.000",
  ReportName: "test report 3",
  Posted: "Y",
  Seq: "01",
},
];

I want to filter out anything in array 'b', that does not contain ALL 4 of the exact values in in one of the objects within Array 'a'.

So, as per the examples above, I would be left with:

// from array 'b'
const c = [
{
  Employee: "00001",
  EmplName: "William Apple",
  ReportDate: "2021-03-10T00:00:00.000",
  ReportName: "test report",
  Posted: "Y",
  Seq: "01",
}
];

I have looked at various ways to do this. I cannot just check to see if the objects are equal, as array 'a' will only contain 4 value/keys, and array 'b' will have many more / a dynamic amount.

Would using the Filter method through a loop and multiple IF statements be the best approach?

3 Answers

You can iterate over the items of a using .reduce and store them in a Map where Employee is the key and the other attributes (Seq, ReportDate, ReportName) are the value.

After that, use .filter to get the items from b where their Employee exists in the map and the other three attributes match.

const a = [
  { Employee: "00001", Seq: "01", ReportDate: "2021-03-10T00:00:00.000", ReportName: "test report" },
  { Employee: "00002", Seq: "01", ReportDate: "2020-03-10T00:00:00.000", ReportName: "test report 2" }
];
const b = [
  { Employee: "00001", EmplName: "William Apple", ReportDate: "2021-03-10T00:00:00.000", ReportName: "test report", Posted: "Y", Seq: "01" },
  { Employee: "00003", EmplName: "John Smith", ReportDate: "2021-03-10T00:00:00.000", ReportName: "test report 3", Posted: "Y", Seq: "01" }
];

const employeesMap = a.reduce((map,employee) => {
  const { Employee, ...attributes } = employee;
  map.set(Employee, attributes);
  return map;
}, new Map);

const res = b.filter(employee => {
  const { Employee, Seq, ReportDate, ReportName } = employee;
  const employeeFromA = employeesMap.get(Employee);
  return employeeFromA && 
    employeeFromA.Seq === Seq && 
    employeeFromA.ReportDate === ReportDate && 
    employeeFromA.ReportName === ReportName;
});

console.log(res);

I would separate out the test for an exact object match from the looping code like this:

const subObject = (a, b) => 
  Object .entries (a) .every (([k, v]) => b [k] == v)

const matches = (xs, ys) => 
  ys .filter (y => xs .some (x => subObject (x, y)))

const a = [{Employee: "00001", Seq: "01", ReportDate: "2021-03-10T00:00:00.000", ReportName: "test report"}, {Employee: "00002", Seq: "01", ReportDate: "2020-03-10T00:00:00.000", ReportName: "test report 2"}]
const b = [{Employee: "00001", EmplName: "William Apple", ReportDate: "2021-03-10T00:00:00.000", ReportName: "test report", Posted: "Y", Seq: "01"}, {Employee: "00003", EmplName: "John Smith", ReportDate: "2021-03-10T00:00:00.000", ReportName: "test report 3", Posted: "Y", Seq: "01"}]

console .log (matches (a, b))

subObject checks whether every property of a has the same value in b.

matches uses that to filter the second list based on whether the first list has some element which is a subobject of it.

Both turn out to be pretty simple functions. While we could inline subObject inside matches, I think it's useful on its own, and makes for nicer code like this.

const results = b.filter(bv => !!a.find(av => Object.keys(av).filter(ak => av[ak] === bv[ak]).length === Object.keys(av).length));

console.log(results);

This filters the "b" array down to only those elements that also have a match in the "a". A "match" is determined here by all of the key/values from the "a" item matching all the corresponding key/values in the "b" item. The extraneous keys in the "b" item are ignored.

Related