下划线 - 在 _.find 之后安全地得到 属性

Underscore - safely get property after _.find

正如标题所说,我正在尝试在找到后安全地获取 属性 of object。

假设我有这样的数组:

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

所以我想通过 id 获取一位尊敬的绅士的名字,所以我使用 _.find

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

我正在寻找一种在 _ 链中安全获取 name 的方法。类似于:

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

Ofc 我可以用 if(person && person.name) 之类的检查来做到这一点...或者甚至可以编写我自己的原型方法,但不想重新发明轮子。

当您使用 get 时,我假设您使用的是 lodash 而不是下划线。如果是这样,那么您可以使用版本 4.14.0 中引入的 defaultTo

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>

我相信 是正确的,因为您似乎使用的是 Lodash,而不是 Underscore。我还发现该答案中提供的解决方案非常优雅,但我想展示一个替代方案。

注意:我将提供一个下划线答案,因为这个问题被标记为这样,但也是一个 Lodash 问题,因为 OP 似乎对此感兴趣

Underscore.js

我总是觉得有点尴尬的一件事是您所处的确切情况 - 链(数组)-> 进行操作 -> 查找 -> 做其他事情。 Underscore 中的对象函数略有偏差。不过,我们还是可以试试看:

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>

这样就可以了,但是有点尴尬。你缺少 .get,所以你必须先 pluck -> map -> 来做同样的事情。这不是严格意义上的坏_但它不是最好的。一种替代方法是将您的单个结果保存在一个数组中,这将允许您 运行 其他功能。所以,你可以这样做

_.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();

让我们看看等价物

Lodash

Lodash 要好一些,因为它的语法更流畅一些,而且对对象的处理也更好一些,所以您不会遇到必须将它们保存在一个值数组中的情况。

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>