Finally, ECMAScript has introduced some much-needed object manipulation methods
Photo by Thought Catalog on Unsplash
Introduction
The inspiration for these pieces is simple: it?s the fact that there are still plenty of developers for whom JavaScript is sometimes completely confusing? or, at least, doesn?t always make a whole lot of sense.
But JavaScript still powers almost 95% of the 10 million most popular webpages, according to Wikipedia.
And since it only continues to increase in usage as time goes by, I wanted to provide a bunch of articles and examples of ES6+ features that I use regularly, for other developers to reference.
The aim is for these pieces to be short, in-depth explanations of various improvements to the language that I hope will inspire you to write some really cool stuff using JS. Who knows, you might even learn something new along the way.
This piece will focus on JavaScript objects and some handy new methods concerning keys, values, and the combination of the two that were standardized into the language in the last few years.
Object.keys()
This may sound hard to believe, especially if you?re coming from another programming language like Java, but up until ES2015, there was no built-in method in JavaScript to easily access an object?s property names, otherwise known as its keys.
Really?
Shocking, I know. Prior to this, a library like Lodash might be employed to do something that should be a basic, standardized method.
Happily, the ECMAScript committee got their act together with the release of ES2015, and the method Object.keys() was introduced. The method returns an array of a given object’s own enumerable property names, in the same order as we get with a normal for loop.
Example of Object.keys() on an object:
const resistanceFighter = { name: “John Connor”, age: 30, title: “Resistance Leader”, fight() { return `${this.name} leads the resistance fight against the machines.` }};console.log(Object.keys(resistanceFighter)); // [ ‘name’, ‘age’, ‘title’, ‘fight’ ]
The Object.keys() syntax is very simple: You pass an object, like resistanceFighter, into the method as its parameter, and the method returns the keys, or names, of that object?s properties and methods.
In this case, it returns the three property names: name, age, and title, plus the method attached to the object, fight.
The elements are returned as strings in an array that correspond to the enumerable properties found directly on the object. The ordering of the properties is the same as that given by manually looping over the properties of the object.
Let?s take a look at some other examples of how Object.keys() will interact with various JavaScript data structures.
Object.keys() with a simple array:
const resistanceTeam = [ “Sarah J Connor”, “Terminator”, “John Connor”];console.log(Object.keys(resistanceTeam)); // [ ‘0’, ‘1’, ‘2’ ]
When running the method against an actual array, the keys returned are the ?stringified? indexes of each item contained in the array.
Object.keys() with an array-like object:
const movieTitles = { 0: “The Terminator”, 1: “Judgment Day”, 2: “Rise of the Machines”, 3: “Salvation”, 4: “Genesis”, 5: “Dark Fate”}console.log(Object.keys(movieTitles)); // [ ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’ ]
Similar to a true array, an object that?s like an array with index numbers listed as its keys will return an array of numbers as strings when it?s run through the Object.keys() method.
Object.keys() on an array-like object with random key ordering:
const movieReleaseYears = { 1991: “Judgment Day”, 2015: “Genisys”, 2003: “Rise of the Machines”, 1984: “The Terminator”, 2019: “Dark Fate”, 2009: “Salvation”}console.log(Object.keys(movieReleaseYears)); // [ ‘1984’, ‘1991’, ‘2003’, ‘2009’, ‘2015’, ‘2019’ ]
If you run Object.keys() on an object with randomly ordered keys, like, say, keys that happen to be the years the different Terminator movies were released, the array of keys produced will be ordered from least to greatest.
If, however, you run this same method on an object where the keys are non-numerical strings, they will not be printed out in a sorted order. To get them sorted, you?ll still need to run the .sort() method on the array. See the example below for reference.
Object.keys() on an object with keys, both unsorted and sorted:
const movieDirectorsUnordered = { director2: “Tim Miller”, director4: “Jonathan Mostow”, director1: “James Cameron”, director3: “McG”}console.log(Object.keys(movieDirectorsUnordered)) // [ ‘director2’, ‘director4’, ‘director1’, ‘director3’ ]console.log(Object.keys(movieDirectorsUnordered).sort()); // [ ‘director1’, ‘director2’, ‘director3’, ‘director4’ ]
One more thing you can do with Object.keys() in ES2015: You can coerce non-object things like strings to objects.
Object.keys() coercing a string to an object:
const animal = “canine”;console.log(Object.keys(animal)); // [ ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’ ]
If you try to call this method in ES5 or below, a TypeError will be thrown, just as an FYI.
Now, let?s move on to the other half of the object equation: The values that go with the keys.
Object.values()
For whatever reasons, the method Object.values() wasn?t standardized in the JavaScript language until ES2017 (Why not sooner?)
The Object.values() method returns an array of a given object’s own enumerable property values, in the same order as that provided by a for…in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
If you?ve ever run a relatively strict set of ESLint rules, like Airbnb?s, against your repo, you may have seen a warning about using for…in loops for the reason mentioned above.
Looping through the prototype chain will reveal inherited properties as well as properties that belong to the object itself, which is usually not desired.
Example of Object.values() on an object:
const resistanceFighter = { name: “John Connor”, age: 30, title: “Resistance Leader”, fight() { return `${this.name} leads the resistance fight against the machines.` }};console.log(Object.values(resistanceFighter)); // [ ‘John Connor’, 30, ‘Resistance Leader’, [Function: fight] ]
Using the same resistanceFighter object I used in the Object.keys() example above, when I call Object.values() and pass it as the parameter, I get back an array of the values in the object: ‘John Connor’, 30, ‘Resistance Leader’, and the fight() function.
Unlike the array of strings thatObject.keys() returns, Object.values() returns an array whose elements are the actual enumerable property values found on the object. Once again, the ordering of the properties is the same as that given by looping over the property values of the object manually.
Here are some examples of Object.keys() in action.
Object.values() with a simple array:
const resistanceTeam = [ “Sarah J Connor”, “Terminator”, “John Connor”];console.log(Object.values(resistanceTeam)); // [ ‘Sarah J Connor’, ‘Terminator’, ‘John Connor’ ]
Just as when Object.keys() prints out the index of each item in the array, calling Object.values() prints out the actual items in the array, just as they are currently listed.
Object.values() on an array-like object:
const movieTitles = { 0: “The Terminator”, 1: “Judgment Day”, 2: “Rise of the Machines”, 3: “Salvation”, 4: “Genisys”, 5: “Dark Fate”}console.log(Object.values(movieTitles));/* [ ‘The Terminator’, ‘Judgment Day’, ‘Rise of the Machines’, ‘Salvation’, ‘Genisys’, ‘Dark Fate’ ] */
When Object.values() is called on the array-like object with numbers as its keys, the resulting array is printed out in the order of the keys. This can be proven by the following example.
Object.values() on an array-like object with random key ordering:
const movieReleaseYears = { 1991: “Judgment Day”, 2015: “Genisys”, 2003: “Rise of the Machines”, 1984: “The Terminator”, 2019: “Dark Fate”, 2009: “Salvation”}console.log(Object.values(movieReleaseYears));/* [ ‘The Terminator’, ‘Judgment Day’, ‘Rise of the Machines’, ‘Salvation’, ‘Genisys’, ‘Dark Fate’ ] */
As you can see, even though the movieReleaseYears? keys are in a random order, when the resulting array is printed out from Object.values(), the values are in the numerical order of their keys? values.
This same sorting does not hold true when the object?s keys are non-numerical values.
In that case, as with the Object.keys() example, you must manually sort the resulting array yourself with .sort(). The array will sort according to the values in it, not the keys the values were once attached to.
Object.values() on an object?s values, both unsorted and sorted:
const movieDirectorsUnordered = { director2: “Tim Miller”, director4: “Jonathan Mostow”, director1: “James Cameron”, director3: “McG”}console.log(Object.values(movieDirectorsUnordered)); // [ ‘Tim Miller’, ‘Jonathan Mostow’, ‘James Cameron’, ‘McG’ ]console.log(Object.values(movieDirectorsUnordered).sort()); // [ ‘James Cameron’, ‘Jonathan Mostow’, ‘McG’, ‘Tim Miller’ ]
And just like Object.keys(), Object.values() has the power to coerce non-object primitives like strings into objects.
Behold,Object.values() coercing a string to an object:
const animal = “canine”;console.log(Object.values(animal)); // [ ‘c’, ‘a’, ‘n’, ‘i’, ‘n’, ‘e’ ]
OK, time to go to part three of this piece: The combination of object keys and values, courtesy of Object.entries().
Object.entries()
The Object.entries() method was also introduced with ES2017, and the method returns an array of a given object?s own enumerable string-keyed property [key, value] pairs, in the same order as that provided by a for…in loop.
As I stated above in the Object.values() section, a for-in loop, which could achieve a similar outcome as Object.entries(), iterates over the prototype chain as well, which isn?t usually the desired behavior of the code. This method provides a more recommended way to iterate through an object?s keys and values.
The order of the array returned by Object.entries() does not depend on how an object is defined. If there is a need for certain ordering then the array should be sorted first like Object.entries(obj).sort((a, b) => b[0].localeCompare(a[0]));.
Example of Object.entries() on an object:
const resistanceFighter = { name: “John Connor”, age: 30, title: “Resistance Leader”, fight() { return `${this.name} leads the resistance fight against the machines.` }};console.log(Object.entries(resistanceFighter));/* [ [ ‘name’, ‘John Connor’ ], [ ‘age’, 30 ], [ ‘title’, ‘Resistance Leader’ ], [ ‘fight’, [Function: fight] ] ] */for(let [key, value] of Object.entries(resistanceFighter)){ console.log(`${key}: ${value}`);}/* name: John Connor age: 30 title: Resistance Leader fight: function fight() { return this.name + ” leads the resistance fight against the machines.”;} */Object.entries(resistanceFighter).forEach(([key, value]) => console.log(`${key}: ${value}`))/* name: John Connor age: 30 title: Resistance Leader fight: function fight() { return this.name + ” leads the resistance fight against the machines.”;} */
In the example above, calling Object.entries() on the resistanceFighter object produces an array of nested arrays, each inner array corresponding to one of the object?s key-value pairs.
The second and third parts of the example are a way to elegantly iterate through an object?s values with a for…of loop or the Array.forEach() method, both of which I find particularly useful when I need to debug or transform an object for future use.
Let?s look at some other uses for Object.entries().
Object.entries() with a simple array:
const resistanceTeam = [ “Sarah J Connor”, “Terminator”, “John Connor”];console.log(Object.entries(resistanceTeam));/* [ [ ‘0’, ‘Sarah J Connor’ ], [ ‘1’, ‘Terminator’ ], [ ‘2’, ‘John Connor’ ] ] */
Calling the Object.entries() method on a simple array will print out all the values in the array with their corresponding index values as their keys ? pretty straightforward and similar to calling Object.keys() on an array.
Object.entries() on an array-like object:
const movieTitles = { 0: “The Terminator”, 1: “Judgment Day”, 2: “Rise of the Machines”, 3: “Salvation”, 4: “Genisys”, 5: “Dark Fate”}console.log(Object.entries(movieTitles));/* [ [ ‘0’, ‘The Terminator’ ], [ ‘1’, ‘Judgment Day’ ], [ ‘2’, ‘Rise of the Machines’ ], [ ‘3’, ‘Salvation’ ], [ ‘4’, ‘Genisys’ ], [ ‘5’, ‘Dark Fate’ ] ] */
Similarly, calling the method on an object with index numbers as its names, it will produce an array of arrays with the keys and values of each object property.
Object.entries() on an array-like object with random key ordering:
const movieReleaseYears = { 1991: “Judgment Day”, 2015: “Genisys”, 2003: “Rise of the Machines”, 1984: “The Terminator”, 2019: “Dark Fate”, 2009: “Salvation”}console.log(Object.entries(movieReleaseYears));/* [ [ ‘1984’, ‘The Terminator’ ], [ ‘1991’, ‘Judgment Day’ ], [ ‘2003’, ‘Rise of the Machines’ ], [ ‘2009’, ‘Salvation’ ], [ ‘2015’, ‘Genisys’ ], [ ‘2019’, ‘Dark Fate’ ] ] */
As in previous pieces of this blog, Object.entries() on movieReleaseYears, with its currently unordered number keys, will return a list of arrays sorted by their index values in ascending order.
Object.entries() on an object?s values, both unsorted and sorted:
const movieDirectorsUnordered = { director2: “Tim Miller”, director4: “Jonathan Mostow”, director1: “James Cameron”, director3: “McG”}console.log(Object.entries(movieDirectorsUnordered));/* [ [ ‘director2’, ‘Tim Miller’ ], [ ‘director4’, ‘Jonathan Mostow’ ], [ ‘director1’, ‘James Cameron’ ], [ ‘director3’, ‘McG’ ] ] */console.log(Object.entries(movieDirectorsUnordered).sort());/* [ [ ‘director1’, ‘James Cameron’ ], [ ‘director2’, ‘Tim Miller’ ], [ ‘director3’, ‘McG’ ], [ ‘director4’, ‘Jonathan Mostow’ ] ] */
But, if the object being made into an array of arrays has keys that are strings or something besides numbers that are easy to order, it will just print out the contents as they are listed in the object unless you manually call the .sort() method afterward. Fair enough?
Object.entries() coercing a string to an object:
const animal = “canine”;console.log(Object.entries(animal));/* [ [ ‘0’, ‘c’ ], [ ‘1’, ‘a’ ], [ ‘2’, ‘n’ ], [ ‘3’, ‘i’ ], [ ‘4’, ‘n’ ], [ ‘5’, ‘e’ ] ] */
A final example. A string can be mutated into an object with the help of Object.entries().
As you see in the code above, the string “canine” becomes an array of arrays, with each character in the string becoming the value portion of the array and the index of that string becoming the key portion.
Conclusion
Surprisingly, until the releases of ES2015 and ES2017 JavaScript objects lacked some seemingly basic methods to read and manipulate keys and values. Luckily for us, accessing an object?s keys, its values, and even iterating through those keys and values together is now standard.
Keeping up with all the improvements the ECMAScript committee is coming out with can be more than a little overwhelming, which is why I started writing these pieces.
My goal with this series is to go in-depth on pieces of the ES6 syntax you may use daily (or may not even be aware of), so you can be an even more effective web developer.
Check back in a few weeks, I?ll be writing about more JavaScript and ES6, or something else related to web development.
Thanks for reading, I hope you?ll give ES6’s newest object methods a spin in your JavaScript code ? it will make your development life easier, I promise.
If you enjoyed reading this, you may also enjoy some of my other pieces in this series:
- Classes and Inheritance: JavaScript ES6 Feature Series (Pt 8)
- Built-In Module Imports and Exports: JavaScript ES6 Feature Series (Pt 7)
- Enhanced Object Literal Value Shorthand: JavaScript ES6 Feature Series (Pt 6)
- String Template Literals: JavaScript ES6 Feature Series (Pt 5)
- Spread & Rest Parameters: JavaScript ES6 Feature Series (Pt 4)
- Default Function Parameter Values: JavaScript ES6 Feature Series (Pt 3)
- Arrow Functions: JavaScript ES6 Feature Series (Pt 2)
- Var, Let & Const: JavaScript ES6 Feature Series (Pt 1)
References and Further Resources
- Object.entries(), MDN docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
- Object.keys(), MDN docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
- Object.values(), MDN docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values