Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I display array objects using a simple loop and 'if' statements?

I have got three arrays and a couple of simple loops. I want to specify three conditions that would show a person from Warsaw at a Web developer position with a salary over 2000. The problem is that it shows two records instead of one.

I've tried writing conditions inside each loop, but none of my combination did work.

var people = [
    {'name': 'Viola', 'salary': 2500, 'surname': 'Smith'},
    {'name': 'Boris', 'salary': 1300, 'surname': 'Popkovitch'},
    {'name': 'John',  'salary': 500,  'surname': 'Lynn'},
    {'name': 'Tom',   'salary': 3300, 'surname': 'Gates'},
    {'name': 'Levis', 'salary': 900,  'surname': 'Klark'},
];

var workplace = [
    {'city': 'New York',   'persons': ['Viola']},
    {'city': 'Manchester', 'persons': ['John', 'Boris']},
    {'city': 'Warsaw',     'persons': ['Tom', 'Levis']},
];

var job = [
    {'position': 'Head manager',  'workers': ['Boris']},
    {'position': 'Web developer', 'workers': ['Tom', 'Viola']},
    {'position': 'Principal',     'workers': ['Levis', 'John']}
];

var array = [];

for (var x = 0; x < people.length; x++) {
  for (var y = 0; y < workplace.length; y++) {
    for (var z = 0; z < job.length; z++) {
      if (workplace[y].city === 'Warsaw' && job[z].position === 'Web developer' && people[x].salary > 2000) {
        array.push(people[x]);
      }
    }
  }
};

console.log(array);

I expect the code to return only the Tom object, not Tom and Viola. Any idea?

like image 956
VQS Avatar asked Jun 10 '19 21:06

VQS


1 Answers

Your code actually does this: If city 'Warsaw' exists in the list and position 'Web developer' exists in the list, then get me all people with salary over 2k. Since the first two condition are true upon your sample data (tautology), the code you wrote returns all people from the list with a salary over 2k, which is what you observed and ended up here.

At this point I would suggest you to think whether the data structure you have is suitable for filtering people by those criteria. But let's say you need to stick with the current data representation. Furthermore the code you wrote and Barmar copied is incredibly inefficient. Here's what a (judicious) human would do to complete such a task:

  1. Locate 'Warsaw' in the list of workplaces and highlight it with a marker pen; go to 8., if not found.
  2. Locate 'Web developer' in the list of jobs and highlight it with a marker pen; go to 8., if not found.
  3. Find first person with salary > 2000; go to 8., if not found.
  4. Lookup person name in highlighted city's person list; go to 8., if not found.
  5. Lookup person name in highlighted job's worker list; go to 8., if not found.
  6. Yay, I've found a record that matches the criteria, push it to output!
  7. Find next person with salary > 2000; go to 4., if found.
  8. Done!

Did you see any for-loop in the algorithm above? Well, some would say the loops are hidden in there. And that's true, but nowadays we have higher order functions (I hope you don't mind Python code) that do exactly the same - hide loops in them. An example of such a function is Array.Filter. It takes a callback (delegate, lambda, predicate, arrow function, callitwhatyouwant, etc.) argument that is executed exactly once for each element in the array in order they appear in array. The callback decides whether the specific element should be kept in the resulting array. The result of the function is a new array filled with elements for which the callback function returned true. Let's start building upon this function.

const array = people.filter(person => person.salary > 2000);

Here I passed an arrow function as the parameter, because of its concise syntax. This line of code effectively implements step #3 and #7 of the algorithm above. Here's the code for steps #1 and #2:

const warsaw = workplace.find(aWorkplace => aWorkplace.city === 'Warsaw');
const webDeveloper = workplace && job.find(aJob => aJob.position === 'Web developer');

I used the Array.find function to locate the required records. This, of course, assumes that city name and position name is unique within the array. Do you still recall that point about data structures? But never mind, let's leave it aside. workplace && on the second line is to prevent a pointless lookup, in case when 'Warsaw' is not found. Now to put it all together:

const warsaw = workplace.find(aWorkplace => aWorkplace.city === 'Warsaw');
const webDeveloper = workplace && job.find(aJob => aJob.position === 'Web developer');
const array = (warsaw && webDeveloper && people.filter(person =>
  person.salary > 2000 &&
  warsaw.persons.includes(person.name) &&
  webDeveloper.workers.includes(person.name)
)) || [];

I know I could have omitted warsaw && on the third line, but I prefer keeping it there in order not to introduce a "puzzle" into the logic.

So what have we learned here? Why did we have to go through this? If you compare the original for-loop based code to the one above, you'll quickly find out that the latter is more readable, because it basically written in plain English and is more efficient, because it avoids performing unnecessary steps.

And as a bonus maybe today was the day you learned something about higher order functions and arrow functions.

Here's the snippet.

var people = [
    {'name': 'Viola', 'salary': 2500, 'surname': 'Smith'},
    {'name': 'Boris', 'salary': 1300, 'surname': 'Popkovitch'},
    {'name': 'John',  'salary': 500,  'surname': 'Lynn'},
    {'name': 'Tom',   'salary': 3300, 'surname': 'Gates'},
    {'name': 'Levis', 'salary': 900,  'surname': 'Klark'},
];

var workplace = [
    {'city': 'New York',   'persons': ['Viola']},
    {'city': 'Manchester', 'persons': ['John', 'Boris']},
    {'city': 'Warsaw',     'persons': ['Tom', 'Levis']},
];

var job = [
    {'position': 'Head manager',  'workers': ['Boris']},
    {'position': 'Web developer', 'workers': ['Tom', 'Viola']},
    {'position': 'Principal',     'workers': ['Levis', 'John']}
];

const warsaw = workplace.find(aWorkplace => aWorkplace.city === 'Warsaw');
const webDeveloper = workplace && job.find(aJob => aJob.position === 'Web developer');
const array = (warsaw && webDeveloper && people.filter(person =>
    person.salary > 2000 &&
    warsaw.persons.includes(person.name) &&
    webDeveloper.workers.includes(person.name)
)) || [];

console.log(array);

My final observation is that you used identifier people, which implies plural, and that is OK, but for the remaining lists you used singular nouns - workplace and job. I'd recommend you to keep naming consistency, because it also greatly improves the code readability.

like image 178
Peter Wolf Avatar answered Oct 26 '22 20:10

Peter Wolf