Kindzoku
Kindzoku

Reputation: 1420

Underscore - safely get property after _.find

As the title says, I'm trying to get property of object after find, safely.

Let's assume, that I have such array:

var ppl = [
    { id: 1, name: 'Smith' },
    { id: 2, name: 'Wesson' },
    { id: 3, name: 'Remington' },
    { id: 4, name: 'Colt' }
];

So I'm trying to get a name of one of this respectful gentlemans by id, so I'm using _.find.

var person = _.find(ppl, function(p) { return p.id === 2; });

I'm looking for a way to safely get name in the _ chain. Something like:

var personName = _.chain(ppl)
                  .find(function(p) { return p.id === 2; })
                  .get('name') //<-- can't find appropriate method :(
                  .values();

Ofc I can do it with checks like if(person && person.name)... Or even write my own prototype method, but don't want to reinvent the wheel.

Upvotes: 1

Views: 2019

Answers (2)

Gruff Bunny
Gruff Bunny

Reputation: 27976

As you're using get I'm assuming that you're using lodash and not underscore. If so then you could use defaultTo which was introduced in version 4.14.0:

var ppl = [
  { id: 1, name: 'Smith' },
  { id: 2, name: 'Wesson' },
  { id: 3, name: 'Remington' },
  { id: 4, name: 'Colt' }
];

var nullPerson = {
  id: 0, 
  name: 'null'
}

var personName = _.chain(ppl)
  .find({id: 99})
  .defaultTo(nullPerson)
  .get('name')
  .value();

console.log(personName);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

Upvotes: 2

VLAZ
VLAZ

Reputation: 29004

I believe GruffBunny is correct in that you seem to be using Lodash, rather than Underscore. I also find like the solution provided in that answer to be quite elegant, but I wanted to show an alternative.

Note: I will provide an Underscore answer, since the question is tagged as such but also a Lodash one, since OP seems to be interested in that

Underscore.js

One thing that I always found a bit awkward is the exact situation you're in - chain (an array) -> do operations -> find -> do other things. The Object functions in Underscore are slightly off. However, we can still try to do it:

var ppl = [
    { id: 1, name: 'Smith' },
    { id: 2, name: 'Wesson' },
    { id: 3, name: 'Remington' },
    { id: 4, name: 'Colt' }
];

function findName(id) {
  return _.chain(ppl)
  .findWhere({id: id}) // <- shorter syntax than _.find and does the same as supplying your own function. 
                      // It's even shorter than the already short fat arrow syntax which would be: (p => p.id === 2)
  .pick('name') // <- extract a new object that has a single property "name"
  .map() //<- 1. for objects, it's equivalent to mapping the values only, e.g., _.map(_.values(obj)) 
         //^- 2. if no argument is specified, it defaults to _.identity
  .first() // <- since the previous step returns an array of one value this step unwraps it and leaves you with a plain value
  .value(); // end chain, return result
}

console.log("Find ID = 2:", findName(2));
console.log("Find ID = 42:", findName(42));
console.log('Find ID = "2" (string value):', findName("2"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

So, this will do the job but it is a bit awkward. You're lacking the .get, so you have to pluck -> map -> first to do the equivalent. It's not strictly bad_ but it's not-as-good as it could. One alternative is to keep your single result in an array which will allow you to run additional functions. So, you can do this

_.chain(ppl)
  .where({id: id}) //it's basically "filterWhere" like _.findWhere but it returns all results in an array. In this case, it would be a single result purely so we can do the below
  .map('name') //<- allows you to map by name of the property but only with arrays
  .first() // <- still have to unwrap
  .value();

let's see the equivalent

Lodash

Lodash is a bit better, since it has a bit smoother syntax and also has a bit better handling of objects, so you don't face situations where you have to keep them in an array of one value.

var ppl = [
    { id: 1, name: 'Smith' },
    { id: 2, name: 'Wesson' },
    { id: 3, name: 'Remington' },
    { id: 4, name: 'Colt' }
];

function findName(id, defaultName) {
  return _.chain(ppl)
  .find({id: id}) //<- Lodash does not have _.findWhere but the normal _.find already handles this
  .get('name') // <- see, much simpler! You can also supply a second argument which would be returned as a default value
  .value();
}

console.log("Find ID = 2:", findName(2));
console.log("Find ID = 42:", findName(42));
console.log('Find ID = "2" (string value):', findName("2"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

Upvotes: 2

Related Questions