Array map returning array of undefined values

That if statement is a monster, easily solved by moving the conditions into an isAcademicDepartment() function.

Here’s a nice way to remove duplicates too:

function removeDuplicates(array) {
    const unique = [...new Set(array)];
    return unique;
}
1 Like

Or just an if(bad_departments.includes(user.department)) (and define bad_departments as an array somewhere)

Also note that your current code will return undefined for anyone who doesn’t have a department defined, so it’s kind of redundant to check for an exclude undefineds.

> let fac = [{"test": 1},{"test": 3},{"test": 4},{"test": 5},{"warble":6}]
< undefined
> fac.map(x => { return x.test })
< (5) [1, 3, 4, 5, undefined]
> fac.map(x => { if(typeof x.test != "undefined") { return x.test }})
< (5) [1, 3, 4, 5, undefined]

(map must return an array the same size as the input array; you cannot filter using map)

Just to point out a potential fix for that is flatMap. You can filter by returning an empty array for false.

let fac = [{"test": 1},{"test": 3},{"test": 4},{"test": 5},{"warble":6}]

fac.flatMap((x) => (x.test !== undefined) ? x.test : [])
// [1, 3, 4, 5]

Paul has a point. When you start to write code like that, it is time to question whether there is a better alternative.

How many departments do pass the test? Would that be easier to test, rather than exclusions?

There maybe more perfomant solutions, but I have opted to put the exclusions into an array. That way Array.includes can be used to test against all these exclusions.

const exclusions = [
  'Academic Affairs',
  'Student Affairs',
  'Communications & Marketing',
  'Enrollment Management',
  'Enterprise Information Technology',
  'IRPA',
  'Learning and Technology',
  'Human & Environmental Services',
  'Institutional Advancement',
  'Finance'
]

Example:

exclusions.includes('IRPA') // true
exclusions.includes('Commerce') // false

Filter departments function

Then as Paul suggested put the departments test into a function.

const filterDepartments = (faculty) => {
  return faculty.flatMap((user) => {
    const department = user.department
    // if department exists
    if (department) {
      // If the department is in the excluded list return an empty array.
      // This will be filtered out, otherwise return the user
      return (exclusions.includes(department)) ? [] : user
    }
    // There is no department, return an empty array.
    // Again this will be filtered out.
    return []
  })
}

A quick test

const faculties = [
  { id: 3, department: 'Academic Affairs' },
  { id: 2, department: 'Economics' },
  { id: 3, department: 'IRPA' },
  { id: 4, department: 'Commerce' },
  { id: 5, department: '' },
  { id: 6 }
]

console.log(filterDepartments(faculties))
/*
[
  {id: 2, department: 'Economics'},
  {id: 4, department: 'Commerce'}
]
*/

Edit:
Just an another thought. You may consider changing those exclusions to all lowercase.

Then testing like this

return (exclusions.includes(department.toLowerCase())) ? [] : user

That way it would still pickup on matches with slight typos e.g. ‘Enrollment management’

Thanks! This looks way better. I have one question regarding the earlier filter of the facultyBios and facultyTitles. This function is returning undefined for all the facultyBios that don’t exist within the facultyTitles (there is no matching “name” property), so I get an array of foundUsers with some of the objects as undefined. What is causing this?

function getFacultyUsers(bios, titles) {
  const userBios = bios[0].users;
  const userTitles = titles[0].users;

  // return all the users
  return userBios.map((userBio) => {
    const targetName = userBio.name;

    // find user object with same name
    const foundUser = (userTitles.find(({ name, title}) => name === targetName && title != undefined && title.includes("Professor")));

    if (foundUser === undefined) return ;
    
    const image = userBio.image;
  
      if(image === undefined) {
          return;
      }
   
   // match all tests return with title
   return { ...userBio, title: foundUser.title }
  })
}

In the userBios.map(), there are a couple of return statements.

if (foundUser === undefined) return ;
and
if(image === undefined) { return; }

Those are resulting in the undefined parts.
Are you wanting those undefined parts to be removed?

That can be done by filtering the map afterwards.

  // return all the users
  const usersWithImage = userBios.map((userBio) => {
    ...
  });
  return usersWithImage.filter((user) => user);

Instead of using map and filter, you could alternatively use reduce.

Here’s how that could work:

  return userBios.reduce((facultyUsers, userBio) => {
    ...
    if (foundUser === undefined) {
        return facultyUsers;
    }
    ...
    if(image === undefined) {
        return facultyUsers;
    }
    ...
    facultyUsers.push({ ...userBio, title: foundUser.title });
    return facultyUsers;
  }, []);

At https://jsfiddle.net/p3heu1o5/ there is some sample code that uses the following functions, for comparison sake:

  • getFacultyUsers() function
  • getFacultyUsers_mapFilter() function
  • getFacultyUsers_reduce() function

Hi,

As mentioned before you can use flatMap

Instead of just return use return [] to return an empty array and this will be filtered out.

Example

const arr = [5, 2, 3, 4, 1, 6, 7, 2]
// filter numbers larger than 3 and double them
arr.flatMap((num) => (num > 3) ? num * 2 : [])

[10, 8, 12, 14]

Here is your code using flatMap

function getFacultyUsers(bios, titles) {
  const userBios = bios[0].users;
  const userTitles = titles[0].users;

  // return all the users
  return userBios.flatMap((userBio) => {
    const targetName = userBio.name;

    // find user object with same name
    const foundUser = (userTitles.find(({ name, title}) => name === targetName && title != undefined && title.includes("Professor")));

    if (foundUser === undefined || userBio.image === undefined) return [];

    return { ...userBio, title: foundUser.title }
  })
}

Edit:

This line is quite long and for readability might benefit from refactoring

const foundUser = (userTitles.find(({ name, title}) => name === targetName && title != undefined && title.includes("Professor")));

Refactored

const foundUser = userTitles.find(
  ({ name, title }) => {
    if (name !== targetName) return false;
    
    return title != undefined && title.includes('Professor');
  }
)
2 Likes

Thanks so much for all your help with this! You have definitely helped me to start thinking about how to write my JavaScript.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.