Home > OS >  How to filter nested array of objects in javascript?
How to filter nested array of objects in javascript?

Time:11-11

I need to filter array of objects with the existing filter, for example

const students= [
    {
        id: 1,
        name: "Alan",
        subjects: [
            {
                name: "math",
                mark: 5
            },
            {
                name: "poetry",
                mark: 4
            },
            {
                name: "physics",
                mark: 3
            }
        ]
    },
    {
        id: 2,
        name: "Joe",
        subjects: [
            {
                name: "geography",
                mark: 5
            },
            {
                name: "music",
                mark: 5
            },
            {
                name: "literature",
                mark: 3
            }
        ]
    },
    {
        id: 3,
        name: "Megan",
        subjects: [
            {
                name: "math",
                mark: 4
            },
            {
                name: "physics",
                mark: 3
            }
        ]
    }
];
const filters = [
  {
    name: "math",
    mark: 5
  },
  {
    name: "physics",
    mark: 3
  }
];

From students array I need to get Alan and Megan according to existing filters. So if at least one subject from filters with mark exists in students, I need to get that student.

I tried this

const common: any = students.filter((x: any) => {
      return filters.indexOf(x.skills) !== -1;
    });
     console.log(common);

But the common array is empty so it didn't work

CodePudding user response:

Something like this?

const students= [
    {
        id: 1,
        name: "Alan",
        subjects: [
            {
                name: "math",
                mark: 5
            },
            {
                name: "poetry",
                mark: 4
            },
            {
                name: "physics",
                mark: 3
            }
        ]
    },
    {
        id: 2,
        name: "Joe",
        subjects: [
            {
                name: "geography",
                mark: 5
            },
            {
                name: "music",
                mark: 5
            },
            {
                name: "literature",
                mark: 3
            }
        ]
    },
    {
        id: 3,
        name: "Megan",
        subjects: [
            {
                name: "math",
                mark: 4
            },
            {
                name: "physics",
                mark: 3
            }
        ]
    }
];

  const filters = [
    {
      name: "math",
      mark: 5
    },
    {
      name: "physics",
      mark: 3
    }
  ];


const results = [];

for (const student of students) {
  for (const subject of student.subjects) {
    const check = filters.some(filter => {
      return filter.name == subject.name && filter.mark == subject.mark;
    });
    if (check) results.push({name: student.name,subject: subject.name,mark: subject.mark});
  }
}

console.log(results);

// Output:
// [
//   {
//     "name": "Alan",
//     "subject": "math",
//     "mark": 5
//   },
//   {
//     "name": "Alan",
//     "subject": "physics",
//     "mark": 3
//   },
//   {
//     "name": "Megan",
//     "subject": "physics",
//     "mark": 3
//   }
// ]

CodePudding user response:

One (kind of silly, but) simple way is to apply a filter() to the array and then stringify the subjects and filters for comparison. Because two identical (but separate) objects will never evaluate to equal each other, we can't just compare the object. One common alternative is to loop through each of the objects properties for comparison, but in this specific case it's not needed.

const students = [
  {
    id: 1,
    name: "Alan",
    subjects: [
      { name: "math", mark: 5 },
      { name: "poetry", mark: 4 },
      { name: "physics", mark: 3 }
    ]
  },
  {
    id: 2,
    name: "Joe",
    subjects: [
      { name: "geography", mark: 5 },
      { name: "music", mark: 5 },
      { name: "literature", mark: 3 }
    ]
  },
  {
    id: 3,
    name: "Megan",
    subjects: [
      { name: "math", mark: 4 },
      { name: "physics", mark: 3 }
    ]
  }
],
filters = [
  { name: "math", mark: 5 },
  { name: "physics", mark: 3 }
]

let common = students.filter(s => {
  let keep = false
  filters.forEach(f => {
    if(JSON.stringify(s.subjects).includes(JSON.stringify(f))) keep = true
  })
  return keep
})

console.log(common)

Is this the best way to do this, probably not. However, given what OP has asked for, this does work and is an interesting way to simplify comparing objects.

CodePudding user response:

You can use Array#filter and Array#some methods as follows:

const 
    students = [ { id: 1, name: "Alan", subjects: [ { name: "math", mark: 5 }, { name: "poetry", mark: 4 }, { name: "physics", mark: 3 } ] }, { id: 2, name: "Joe", subjects: [ { name: "geography", mark: 5 }, { name: "music", mark: 5 }, { name: "literature", mark: 3 } ] }, { id: 3, name: "Megan", subjects: [ { name: "math", mark: 4 }, { name: "physics", mark: 3 } ] } ],
    filters = [ { name: "math", mark: 5 }, { name: "physics", mark: 3 } ],
    
    output = students.filter(
        ({subjects}) => filters.some(
            ({name,mark}) => subjects.some(
                ({name:n,mark:m}) => n === name && m === mark
            )
        )
    );
    
console.log( output );

  • Related