I would change one in my first decision - the parameter names in the _.each() . In fact, you only need one parameter, like the second version:
var arr = []; _.each(a,function(o){ arr.push(_.pick(o,'name','id')); });
But your second solution using _.map() - very clean and it seems perfectly fine.
The first version actually works like _.map() inside. Here is the source code for _.map() :
// Return the results of applying the iterator to each element. // Delegates to **ECMAScript 5** native `map` if available. _.map = _.collect = function(obj, iterator, context) { var results = []; if (obj == null) return results; if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); each(obj, function(value, index, list) { results.push(iterator.call(context, value, index, list)); }); return results; };
As you can see, this is basically the same code as your first version, as well as some additional material.
You asked about refusing keys; you can do this with the _.omit() function, which is the opposite of _.pick() : you specify the names of the properties that you want to omit, and not the ones you want to copy. Thus, with your test data, this will lead to the same as other versions:
_.map(a, function(o) { return _.omit(o, 'description'); });
The difference would be that if there were additional properties in the input, this version would also copy these properties to the output file. A version using _.pick() would only have id and name in the output.
My only additional assumption was that if you are dealing with very large arrays, it would be a little faster to execute the _.pick() equivalent yourself:
var result = _.map( a, function( o ) { return { name: o.name, id: o.id }; });
Or even faster to use your own loop:
var result = []; for( var i = 0, n = a.length; i < n; ++i ) { var o = a[i]; result[i] = { name: o.name, id: o.id }; }
But for a small array, you can also go with simpler code.