在纯JavaScript中与Underscore _.pluck等效的方法是什么?

46

我正在尝试使用纯JS重新创建Underscore pluck函数。然而,我一直得到一个由未定义的数组返回,而不是来自数组对象属性的实际值。

在检查另一个线程 这里 ,我发现你可以通过以下代码在jQuery中重现它...

$.pluck = function(arr, key) { 
    return $.map(arr, function(e) { return e[key]; }) 
}

...但是我在纯JS中复现它时遇到了困难。我尝试了以下方法,但是它对我来说只返回一个未定义的数组。

var pluck = function(arr,key){
  var newArr = [];
  for (var i = 0, x = arr.length; i < x; i++){
    if (arr[i].hasOwnProperty(key)){
      newArr.push(arr[i].key)
    }
  }
  return newArr;
}

因此,目标如下,只是不使用下划线_.pluck,而是使用JS函数名称,例如。 var pluck = function(arr,key){...}

var Tuts = [{name : 'NetTuts', niche : 'Web Development'}, {name : 'WPTuts', niche : 'WordPress'}, {name : 'PSDTuts', niche : 'PhotoShop'}, {name : 'AeTuts', niche : 'After Effects'}];
var niches = _.pluck(Tuts, 'niche');

console.log(niches);

// ["Web Development", "WordPress", "PhotoShop", "After Effects"]

有人能指导我朝正确的方向吗?


2
Underscore的好处在于其源代码是以注释形式在线可用的,因此很容易阅读并了解它们的作用。这里是_.pluck()的源代码,虽然它主要由_.map()和_.property()组成。 - Paul D. Waite
除此之外,现在还有一个带注释源代码的模块化版本。这里是_.pluck模块 - Julian
10个回答

79

在ES5中:

function pluck(array, key) {
  return array.map(function(obj) {
    return obj[key];
  });
}

在ES6中:

function pluck(array, key) {
  return array.map(o => o[key]);
}

7
对于更简单的ES2015版本加一。更简单的方法是使用theArray.map(item => item.theKey)而不创建任何pluck函数。 - Alejandro García Iglesias
1
我喜欢ES2016版本。一旦你习惯了lambda函数,它们就像猫薄荷一样让人上瘾。 - Adrian Moisa
6
const pluck = (arr, key) => arr.map(o => o[key])这段代码的意思是定义一个名为 pluck 的函数,它接受两个参数:一个数组 arr 和一个键名 key。该函数使用 map 方法对数组中的每个对象进行遍历,并返回由所有对象中指定键名的值组成的新数组。 - RobertPitt
你如何在不使用.map的情况下实现这个功能? - ConstantFun
如果我想要从多个键中提取数据并返回一个对象数组,ES6 版本会是什么样子? - Razvan Zamfir

19

你可以使用原生的JavaScript .map()方法来实现:

Array.prototype.pluck = function(key) {
  return this.map(function(object) { return object[key]; });
};

编辑 — 修改内置原型对象应谨慎进行;如果您一般情况下愿意这样做,更好的方法是使用 Object.defineProperty 添加函数,以便可以将其设置为不可枚举:

Object.defineProperty(Array.prototype, "pluck", {
    value: function(key) {
        return this.map(function(object) { return object[key]; });
    }
});

@NULL 没有真正的“需要”,但如果.map()在原型上,那么.pluck()对称存在也是合理的。不过我会修正答案并提供正确的添加方式。 - Pointy
@NULL 我同意,但这要视情况而定。我不喜欢使用能够执行此操作的库(虽然我曾经非常喜欢Prototype),但如果我有信心在项目中是唯一实现此操作的人,这会很方便并且可以使代码更清晰。然而,我只使用过这个技巧极少数次。 - Pointy

8
你离成功很近了。你需要做的是改变以下内容:

newArr.push(arr[i].key);

to:

newArr.push(arr[i][key]);

考虑以下内容:

var obj = { myKey: 'my Value', theKey: 'another value' };
var theKey = 'myKey';

alert(obj.theKey); // another value
alert(obj[theKey]); // my Value
// You can also send in strings here:
alert(obj['theKey']); // another value

希望您能理解我的观点。

1
太棒了,谢谢!我已经让它运行起来了……而且我相信我现在理解了这个概念。每个arr[i]对象并不一定都有.key属性……相反,需要使用方括号表示法传递传递给pluck函数的键值,确保尝试访问作为第二个参数给出的对象的正确属性。这样说对吗? - wesleysmyth
1
是的,当使用方括号表示法时,您可以使用变量作为要访问的键,而使用点表示法时,键将是您编写的内容。这在处理具有点、空格保留字等键时也会对您有所帮助。obj['my weird key...'] 是有效的,而 obj.my weird key... 将抛出语法错误。当针对 < IE9 时,您还需要编写 obj['enum']obj['catch'] - Andreas Louv

2
这里是一个关于如何执行_.pluck(Tuts, 'name');的例子。
const Tuts = [{name : 'NetTuts', niche : 'Web Development'}, {name : 'WPTuts', niche : 'WordPress'}, {name : 'PSDTuts', niche : 'PhotoShop'}, {name : 'AeTuts', niche : 'After Effects'}];


const pluckedNames = Tuts.map(({ name }) => name);

如果您想在对象中没有“name”时删除未定义的值,则可以在之后使用.filter()
const pluckedNames = Tuts.map(({ name }) => name).filter(i => i);

1

不需要使用方法,就可以简单明了:

var pluck = function (arr, key) {
  var newArr = [];
  for (let i = 0; i < arr.length; i++) {
    newArr.push(arr[i][key]);
  }
  return newArr;
};


0

这里有一个可行的解决方案

function pluck(array,key){
  var a = [],
     i1 = -1, i2 = array.length,
      o ;

    while(++i1 < i2)
    {
        o = array[i1] ; // you have to use the bracket operator here
        if(o.hasOwnProperty(key)) a[a.length] = o[key] ;
    }
  return a ;
}

我实际上没有做太多的改变。你的代码失败的原因是因为你使用了点操作符.key来访问数组元素的命名属性,而不是使用方括号操作符[key]。当使用引用作为键时,你需要使用方括号操作符。

你为什么不使用 push() - epascarello

0

使用代理怎么样?

f = new Proxy(x => x, {
    get: (_,prop) => x => x[prop]
})

这是一个关于编程的例子,做 _.pluck(Tuts, 'name');。
const Tuts = [{name : 'NetTuts', niche : 'Web Development'}, {name : 'WPTuts', niche : 'WordPress'}, {name : 'PSDTuts', niche : 'PhotoShop'}, {name : 'AeTuts', niche : 'After Effects'}];
const pluckedNames = Tuts.map(f.name);

如果你想在对象中没有名称的情况下删除未定义的值,可以使用 .filter() 方法。
const pluckedNames = Tuts.map(f.name).filter(f);

0

这是一个结合函数,用于一些需要带键值的对象的情况:

const pluck = (array, value, key = null) => {
    const newArray = key ? {} : [];

    array.forEach((item) => {
        if (key) {
            newArray[item[key]] = item[value];
        } else {
            newArray.push(item[value]);
        }
    });

    return newArray;
}

例子:

const a = [
    {key1: 'value1', key2: 'value2'}, 
    {key1: 'value3', key2: 'value4'},
];
const result = pluck(a, 'key2', 'key1');

它将返回:

{value1: 'value2', value3: 'value4'}

-1

怎么样使用 reduce 函数:

$.pluck = function(arr, key) { 
    return arr.reduce(function(p, v) { 
      return p.concat(v[key]); 
    }, []); 
}

var people = [
  { name: 'James', age: 26 }, 
  { name: 'Fred', age: 56 }
];

$.pluck(people, 'age');
=> [26, 56]

$.pluck(people, 'name');
=> ['James', 'Fred']

-4
var array = {
name: ["Pedro", "João", "Francisco", "Diogo"],
ID: [1,2,3,4]
};
console.log(pluck(array,'name'));

function pluck(array, key) {
   return (array[key]);
};

JSFiddle

你可以使用这个函数,在 JSFiddle 中查看示例!:)


1
这并没有帮助,通常数组将被构建为[{id: 1, name: 'Pedro'}, {id: 2, name: 'Joao'}]这样的结构。 - brettwhiteman
变量数组 = [{ name: ["Pedro", "João", "Francisco", "Diogo"], ID: [1,2,3,4] },{ name: ["Jose", "Maria", "Bruno", "Rui"], ID: [1,2,3,4] }]; console.log(pluck(array[1],'name'));function pluck(array, key) { return (array[key]); }; 现在有帮助了吗? - Pedro Monteiro Arantes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接