Today, we’ll deal with objects, for objects are wonderful! Everything be true will have us write a function that takes in 2 arguments. The first will be an array of objects (also called a collection), and the second will be a predicate that we need to test against the collection.
We will need to check if every property defined in the seconds argument is present on every item of the collection and return either true or false. Here’s a few examples:
var myCollection = [ { name: 'George', age: '24', favColor: 'Blue'}, { name: 'Jack', age: '50', favColor: 'Red'}, { name: 'John', age: '36', favColor: 'Blue'}, { name: 'Andrew', favColor: 'Orange'} ]; var firstPredicate = 'name'; every(myCollection, firstPredicate); //-> true, name is defined for every object. var secondPredicate = 'age'; every(myCollection, secondPredicate); //-> false, Andrew does not have an age defined! // We can also pass in an object though! var firstPredicate = { favColor: 'Blue' }; every(myCollection, name); //-> false, not everyone likes blue!
As you can see, if we only pass in a property name, we check if every object has that property defined; but, if we pass an object, we need to check if the property is defined, and if the values are equal. We’ll be using two methods that deal with objects for the task at hand, Object.prototype.hasOwnProperty() and Object.getOwnPropertyNames().
Object.prototype.hasOwnProperty() will operate over an object and return a boolean value (true/false) if the given property exists within it. Let’s see it in action:
var myObject = { type: 'Car', brand: 'BMW', model: '120d' }; myObject.hasOwnProperty('brand'); //-> true myObject.hasOwnProperty('color'); //-> false
Object.getOwnProperties() will take an object as argument, and return an array listing the existing property names like so:
var myObject = { firstname: 'Bob', lastname: 'Johnson', age: 42, likes: 'Football' }; Object.getOwnPropertyNames(myObject); //-> ['firstname', 'lastname', 'age', 'likes'];
Now that that’s out of the way, let’s get this function rolling. We’ll start by defining the behaviour for the function:
- If the second parameter is a string, check if it exists on every object in the collection.
- If second parameter is an object, check that each property exists on every object in the collection, and that they share the same value.
Here we go with the bare-bones:
function every(collection, pre) { if (typeof pre === 'string') { // Check if property exists on every object. } else if (typeof pre === 'object') { // Check if property exists, and check if values match. } }
Now, we can actually create this function (funnily called every()) by using the Array.prototype.every() or Array.prototype.some() method, but I can’t but feel like cheating, since this bonfire is trying to have us create this method, so we’ll do it in another way, and later on, I’ll show you how the version using Array.prototype.every() looks like, for giggles.
I talk -type- too much, let’s start implementing some logic into our function. We’ll start with the first block of the if statement. We’ll loop
over the collection and return false as soon as an object doesn’t have a property named after our predicate:
function every(collection, pre) { if (typeof pre === 'string') { for (var i = 0; i < collection.length; i++) { if (!collection[i].hasOwnProperty(pre)) { return false; }; } } else if (typeof pre === 'object') { // Check if property exists, and check if values match. } return true; // If no false was returned, return true. }
Now, onto the else if block. Let’s keep in mind, that the object could have many properties and not just one like in the examples, so we must loop over each one of them:
function every(collection, pre) { if (typeof pre === 'string') { for (var i = 0; i < collection.length; i++) { if (!collection[i].hasOwnProperty(pre)) { return false; }; } } else if (typeof pre === 'object') { var propertyList = Object.getOwnPropertyNames(pre); for (var i = 0; i < propertyList.length; i++) { for (var j = 0; i < collection.length; j++) { if (!collection[j].hasOwnProperty(propertyList[i]) || collection[j][propertyList[i]] != pre[propertyList[i]]) { return false; } } } } return true; }
Oh god, what a horrendous amalgamation of nested for loops and one letter variables! This thing works, but it’s pretty hard to read, don’t you think? We could accomplish this same task in a million different ways, but I made it specially messy so you can appreciate the following two solutions. Here’s two functions, one of them uses every(), the other uses some(). To get some insight into what these do, you can check this and that. Now then, take a look at them, give it some thought, and let’s see if you can guess which one does less unnecessary work!
Using Array.prototype.every():
function every(collection, pre) { if (typeof pre === 'string') { return collection.some(function(object) { return object.hasOwnProperty(pre); }); } else if (typeof pre === 'object') { var propertyList = Object.getOwnPropertyNames(pre); return propertyList.every(function(property) { return collection.every(function(object) { return object[property] == pre[property]; }); }); } }
Using Array.prototype.some():
function every(collection, pre) { if (typeof pre === 'string') { return !collection.some(function(object) { return !object.hasOwnProperty(pre); }); } else if (typeof pre === 'object') { var propertyList = Object.getOwnPropertyNames(pre); return propertyList.every(function(property) { return !collection.some(function(object) { return object[property] != pre[property]; }); }); } }
Happy coding! If you have any questions or doubts, give me a shout via email, comment or twitter!