简体   繁体   中英

Extract object from an array if has predefined key

I have array of objects - employeesArray , one object represents employee. Assume that one object has following structure:

{
   "name": "blabla", 
   "id": 1, 
   "position": {
      "code": "codeId", 
      "positionString": "CEO"
   }
}

I have another array of objects - positionsArray , one object represents position. Assume that one object has following structure:

{
   "code": "codeId", 
   "positionString": "CEO"
}

Assume that employeesArray has 100 employees and positionsArray has 15 positions. I want to extract employees with positions existing in positionsArray into another array.

employee.position.code or employee.position.positionString must exist in positionsArray .

How to achieve that?

You could use Sets to have an efficient way to check if a code or position string occurs in the positionsArray:

 function findEmployees(employeesArray, positionsArray) { const codes = new Set(positionsArray.map( ({code}) => code )), positionStrings = new Set(positionsArray.map( ({positionString}) => positionString )); return employeesArray.filter( ({position: {code, positionString}}) => codes.has(code) || positionStrings.has(positionString) ); } // Sample data var employeesArray = [{ "name": "John", "id": 1, "position": { "code": "unknown", "positionString": "CEO" } }, { "name": "Helen", "id": 2, "position": { "code": "9999", "positionString": "Deputy" } }, { "name": "Joy", "id": 3, "position": { "code": "9876", "positionString": null } }, { "name": "Vicky", "id": 4, "position": { "code": "8888", "positionString": "Director General" } }, { "name": "Jack", "id": 5, "position": { "code": "0001", "positionString": "Clerk" } }]; var positionsArray = [{ "code": "0123", "positionString": "CEO" }, { "code": "9876", "positionString": "Director" }, { "code": "5555", "positionString": "Head of Unit" }]; console.log(findEmployees(employeesArray, positionsArray));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

Step 1: Make a loop for each element in positionArray Step 2: Inside of loop body, filter objects using filter command Step 3: Put filtered employees in another array.

var result = [];
for (var i = 0; i < positionArray.length; i++) {
    var filteredEmployees = employeesArray.filter(function(emp){
        return emp.code == positionArray.code;
    });

    //Now put filtered employees into new array
    for (var t = 0; t < filteredEmployees.length; t++) {
       result.push(filteredEmployees[t]);
    }    
}

with Array.reduce one also has a nice tool at hand ... a solution then might look like that ...

 // Sample data var employeesArray = [{ "name": "John", "id": 1, "position": { "code": "unknown", "positionString": "CEO" } }, { "name": "Helen", "id": 2, "position": { "code": "9999", "positionString": "Deputy" } }, { "name": "Joy", "id": 3, "position": { "code": "9876", "positionString": null } }, { "name": "Vicky", "id": 4, "position": { "code": "8888", "positionString": "Director General" } }, { "name": "Jack", "id": 5, "position": { "code": "0001", "positionString": "Clerk" } }]; var positionsArray = [{ "code": "0123", "positionString": "CEO" }, { "code": "9876", "positionString": "Director" }, { "code": "5555", "positionString": "Head of Unit" }]; var result = employeesArray.reduce(function (collector, employee) { if (collector.positions.some(collector.doesMatchBoundEmployee, employee)) { collector.employees.push(employee); } return collector; }, { doesMatchBoundEmployee: function (position) { return ( (position.code === this.position.code) || (position.positionString === this.position.positionString) ); }, positions: positionsArray, employees: [] }).employees; console.log("result : ", result);
 .as-console-wrapper { max-height: 100%!important; top: 0; }

Here is another solution, in functional style:

// Sample data
const employeesArray = [
  { "name": "John", "id": 1, "position": { "code": "unknown", "positionString": "CEO" } },
  { "name": "Helen", "id": 2, "position": { "code": "9999", "positionString": "Manager" } },
  { "name": "Joy","id": 3, "position": { "code": "9876", "positionString": "Manager" } },
  { "name": "Vicky", "id": 4, "position": { "code": "8888", "positionString": "Director" } },
  { "name": "Jack", "id": 5, "position": { "code": "0001", "positionString": "Manager" } }
]

const positionsArray = [
  { "code": "0123", "positionString": "CEO" },
  { "code": "9876", "positionString": "Director" },
  { "code": "5555", "positionString": "Manager" }
]

// we are going to step through the positions array
// and examine positionsArray.positionString
// and produce an object that has one property for each position
const extractPositions = positionsArray.reduce((all, position) => {
  // set the position to a variable
  const title = position.positionString
  // now we are going to filter out every user that has the title
  // and set the result of that to the title property on the object we are creating
  all[title] = employeesArray.filter((e) => e.position.positionString === title)
  // return all is needed because we are accumulating the results
  return all
}, {}) // {} is where we define we are creating an object,
       // and 'return all' adds to it every iteration

console.log('CEO', extractPositions.CEO)
console.log('Director', extractPositions.Director)
console.log('Manager', extractPositions.Manager)

Here is how it looks turned into a function you can call by passing in the two arrays:

const sortPositions = (positions, employees) => {
  const final = positions.reduce((all, position) => {
    const title = position.positionString
    all[title] = employees.filter((e) => e.position.positionString === title)
    return all
  }, {})
  return final
}

console.log(sortPositions(positionsArray, employeesArray))

And, here is the extremely cryptic for no reason, max condensed version:

const getEmployees = (positions, employees) =>
  positions.reduce((all, position) => {
    all[position.positionString] = employees.filter((e) =>
      e.position.positionString === position.positionString)
    return all
  }, {})

console.log(getEmployees(positionsArray, employeesArray))

I would choose the second of my 3 examples because the third one, although more condensed will cause you to hate life 6 months from now when you return to the code. The second one doesn't use implicit returns and it also more explicitly helps the person understand the title is important inside the logic.

The basics of what we are doing here is having two arrays, and we need to somehow compare them to eachother, so in my examples, we decided to use the positions array as a dictionary, and we create a list of employees that have each title found in the dictionary.

If there are employees with no title or titles not in the dictionary, they are ignored.

If there are titles that have 0 employees, an empty array will be added to the final result.

It would become more complex if we wanted to also capture the unsorted employees, but to do that, you would probably want to save a copy of the employees array inside your function, and delete each employee as they are found. Any remaining employees in that new array would be added to an unsorted array in the final result. That would be done on the final pass through the reduce function. It would not be trivial in terms of explaining it, but it would only add about 5-10 lines of code.

Here is an example of how reduce works, because I think it is an incredibly useful tool. It is an accumulator, so it is like an iterator (forEach, map) but it allows you to accumulate the results as you go, so you can build a sum, array, or object.

const numbers = [1, 3, 5, 7, 9]

const addNumbers = numbers.reduce((all, item, i) => {
  console.log('\nAll is currently:', all)
  console.log('We are currently looking at:', item)
  console.log('Current index position:', i)
  all = all + item
  return all
}, 0)

console.log('\nThe total is:', addNumbers)

Look at the 0 there at }, 0) . If you change that to [] or {} , you build an array or object instead of summing the total. If you make it [] , you can do inside reduce: all.push('anything') and if you make it {} , you can do all.prop = 'anything' (or as we did all[title] which creates dynamic property names using bracket notation).

It becomes insanely powerful because instead of just {} , you can do { good: [], bad: [] } which allows you to create really complex objects. Each loop through every item in the original array, you can say if (something === 'good') all.good.push(item) etc, and build an object that has super complex properties, and you only have to pass over the original array once .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM