Orbers
Orbers

Reputation: 79

How do I loop through objects only printing the second property using a for in loop?

Currently my for in loop prints the firstName. What if I want it to print only the lastName? How would I modify the for in loop?

var friends = {};

friends.bill = {
  firstName: "Bill",
  lastName: "Gates",
  number: "(206) 555-5555",
  address: ['One Microsoft Way','Redmond','WA','98052']
};

friends.steve = {
  firstName: "Steve",
  lastName: "Jobs",
  number: "(408) 555-5555",
  address: ['1 Infinite Loop','Cupertino','CA','95014']
};

var list = function(obj) {
  for(var prop in obj) {
    console.log(prop);
  }
};

list(friends);

Upvotes: 2

Views: 85

Answers (3)

Scott Sauyet
Scott Sauyet

Reputation: 50807

The answer from dz210 is great: readable, simple, straight to the point and easy to do.

I'm going to suggest something a little harder to do. I'd suggest you build yourself a little toolkit of reusable functions that can help you do even such easy problems more simply.

Building Little Functions

To begin with, your eventual data structure looks something like this:

var friends = {
  bill: {
    firstName: "Bill",
    lastName: "Gates",
    number: "(206) 555-5555",
    address: ['One Microsoft Way','Redmond','WA','98052']
  },
  steve: {
    firstName: "Steve",
    lastName: "Jobs",
    number: "(408) 555-5555",
    address: ['1 Infinite Loop','Cupertino','CA','95014']
  }
}

You need to pull off the two values stored in it, represented as "bill" and "steve". Here's the first pass at a function to extract the values of an object like this:

var values = obj => {
  var results = [];
  Object.keys(obj).forEach(key => results.push(obj[key]));
  return results;
}

Note that this uses the built-in ability to extract the keys of an object. Then for each one you will need to take the lastName property. Here is a simple function to do that:

var getLastName = obj => obj['lastName'];

(I know I could have used dot-notation. Bear with me a minute.)

While this works for this usage, it's clearly very specific. One for firstName would look like this:

var getFirstName = obj => obj['firstName'];

and so on. But if we want to make this reusable, we might create a function that, given a property name, returns a function which accepts an object and retrieves the named property of the object, like so:

var prop = name => obj => obj[name];

Then we have

var getLastName = prop('lastName');

Now for our case, we want to get the last name for every element in our list. Converting one data structure to another of the same shape by applying a function to every data element is something called mapping, and we can write a simple map function:

var map = fn => list => list.map(fn);

(Note that there are reasons to avoid it this style implementation, but it's not bad for a start.)

This allows us to do for instance,

map(n => n * n)([1, 2, 3, 4, 5]); //=> [1, 4, 9, 16, 25]

or

map(str => str.toUpperCase())(['foo', 'bar', 'baz']); //=> ['FOO', 'BAR', "BAZ']

So now to collect the last names of every friend on the list, we can apply map(getLastName) to the result of values(friends):

map(getLastName)(values(friends)); //=> ['Gates', 'Jobs']

Of course we could stop there, but there is still some useful clean-up to do. There is nothing special about last names. We are likely to often want to grab the value of a named property from every element of a list, and it would be nice to give that a useful name. In many places, it's called 'pluck', to represent plucking the named property off each element. Here's a basic implementation:

var pluck = name => map(prop(name));

which allows for

var getLastNames = people => pluck('lastName')(values(people))
getLastNames(friends);  //=> ['Gates', 'Jobs']

But once we have map, we can also simply our implementation of values:

var values = obj => map(key => obj[key])(Object.keys(obj));    

There is also some commonality between pluck and getLastNames. In each case, we call one function, and pass the results of that to another function. In mathematics, this is called composition, and we can write another function to represent it. This function gets at the heart of the style code we're developing here, combining two functions into one for reusability:

var compose = (f, g) => x => f(g(x));

Using this, we can rewrite two of our functions:

var pluck = compose(map, prop);
var getLastNames = compose(pluck('lastName'), values);

The Result

// library functions
var map = fn => list => list.map(fn);
var compose = (f, g) => x => f(g(x));
var prop = name => obj => obj[name];
var pluck = compose(map, prop);
var values = obj => map(key => obj[key])(Object.keys(obj));

// application code
var getLastNames = compose(pluck('lastName'), values);

There are two important things to note here:

  • The final output of this still doesn't log your output to the console. That was the request in the question, and it seems ridiculous not to manage to do that. I will show how to do that, but let's note that it's likely that this was not the real purpose of the request. Usually there is no significant purpose in logging to the console. We use this to show that we have the data where we want it, but then we use it in a different way. The functions so far have been pure functions, ones that calculate their output based only on their input, not any external sources of information, and ones which create no side effect such as updating the DOM or logging to the console. At some point in your applicaiton you will likely need to have side effects. The style of programming demonstrated here suggests that those be segregated from the rest of your code as much as possible. Those are the ones that make it hard to understand what's happening. The pure functions are easy to follow; these are not. So if the bulk of your code is pure and you isolate the impure stuff, it will make it tremendously easier to test and to debug. But if we want to end up logging our output, we can write:
var log = obj => console.log(obj);
var lastNames = getLastNames(friends); //=> ['Gates', 'Jobs']
lastNames.forEach(log); // logs 'Gates' and then 'Jobs' to the console
  • Although the rest of the functions are reasonably simple, values is fairly complex. This is because we need to use the obj parameter in the first function called and then again in the function that uses that result. There are certainly techniques that would help us do that, but they are more complex than anything done here.

Why Bother?

Why would anyone write this sort of code to solve such a simple problem?

They probably wouldn't.

But small problems multiply and turn into larger ones. There are many ways to handle the growth of one's requirements. What I'm suggesting here is one technique to help manage such complexity. By building oneself a toolkit of simple reusable functions for many of these requirements, one can build bigger and better code on secure foundations.

Moreover, two of the functions discussed, map and compose end up being two of the most prominent functions used in functional programming. If you continued with this technique, you would see them again and again. And the others are all reusable and reasonable ones, with the obvious exception of the one function written specifically for this requirement. But let's look at that one again:

var getLastNames = compose(pluck('lastName'), values);

Once we are used to the notion of composition, this is a very elegant read. We can understand that getLastNames is the composition of two functions, values and pluck('lastName'). The former is clear, and the latter should be straightforward. We don't have to read any code describing how to pluck the the names, or how to compose the two functions. This feels closer to a declaration of what we want the program to do than an explicit set of instructions on how to do it. and that's part of the point. The more we can describe what we want rather than how to do it, the more we are asking the computer to understand us rather than trying to understand the computer.

Can't I Use a Library?

Of course you can. There are many libraries designed around some of these principles. Underscore was designed to bring some functional capabilities to Javascript, and Lodash offers a very similar feature-set. These are two of the most popular JS tools. But both of them adhere to the design of some of the native methods at the expense of what is perhaps a more clean functional design. Other libraries, such as fn.js, fnuc, FKit, and my favorite, Ramda (disclosure: I'm one of the authors), are more straightforwardly functional in their outlook.

Using Ramda, for example, the code would simply be

var getLastNames = R.compose(R.pluck('lastName'), R.values);

But the danger of using a library here is that you lean on it at the expense of understanding what's going on. If you have a good understanding of what compose, and map, what prop and pluck and values do, then using a library, which probably has better-testing, battle-hardened versions of these functions is useful. But if you don't, it might hinder your learning. It never hurts to start by trying these things on your own and then slowly moving to a library.

What is this Style Called?

This technique, of building everything up from simple functions on common data structures, using functions which can accept functions and/or return new functions, is one of the hallmarks of what is known as functional programming. There are many other fairly common features, only two of which we'll discuss. One of the most important is referential transparency, in which any expression can be replaced with its value without altering the behavior of a program. We take a major step toward this by working mostly with pure functions. The other is immutable data structures. While Javascript doesn't support the this feature, we can refuse to modify data that is passed to us and always return new structures. You may note that this is what we've done here. (Although note that by the lines friends.bill = ... and friends.steve = ... in your original question, you're not using an immutable data structure.)

There are many great resources on the web for functional programming, and recently, a number of them are targeting Javascript. It's a good time to be working with this material.

Upvotes: 0

dz210
dz210

Reputation: 768

Please read into the MDN - Working with objects document, from which you can conclude the following:

  1. objectName.propertyName is used to access a specified property within an object. In your case this translates to: obj.bill.lastName and obj.steve.lastName.
  2. When working with many objects within an object, it's a simple as using the previous notation, while looping through the objects. In which case obj[prop] will provide each instance of each object within obj. Accessing the object properties from there is as written on #1.

Result:

var list = function(obj) {
  for(var prop in obj) {
    console.log(obj[prop].lastName);
  }
};

Upvotes: 5

Hatchet
Hatchet

Reputation: 5428

You're passing the object friends into your list function, however, the list function is looping through the properties of the friends object, and not the properties of each friend inside.

To output the lastName property of every friend:

var list = function (obj) {
    for (var friendObj in obj) {
        console.log(obj[friendObj].lastName);
    }
};

Or, to output every property of every friend, change the list function to:

var list = function (obj) {
    for (var friendObj in obj) {
        for (var prop in obj[friendObj]) {
            console.log(obj[friendObj][prop]);
        }
        console.log("");
    }
};

Upvotes: 0

Related Questions