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?
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:
8.
, if not found.8.
, if not found.8.
, if not found.8.
, if not found.8.
, if not found.4.
, if found.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With