相当于_.pick()但对于Lo-dash

时间:2015-05-26 16:38:29

标签: javascript arrays lodash

我有以下系列:

var columns = [
    { key:'url', width:20, type:'text' },
    { key:'title', width:21, type:'text' },
    { key:'desc', width:22, type:'text' },
    { key:'domain', width:23, type:'text' },
    { key:'user', width:24, type:'text' }
];

我正在寻找一种方法来映射带有拾取键的对象数组,这些方法符合以下几点:

_.mapPick(columns, [width]) 
// [{width:20},{width:21},{width:22},{width:23},{width:24}]

我知道我可以像这样扩展lo-dash:

_.mixin({
    mapPick:  mapPick:function (objs,keys){
        return _.map(objs, function (obj) {
            return _.pick(obj,keys)
        })
    }

});

我不确定是否有一些我缺少的原生功能。

我发现了一个类似的问题here,但我正在寻找一种更为低调的本土方式。

6 个答案:

答案 0 :(得分:22)

我认为map() + pick()方法是您最好的选择。您可以编写回调而不是创建内联函数:

_.map(columns, _.partialRight(_.pick, 'key'));

答案 1 :(得分:18)

在我的情况下我还需要强制转换类型,而问题是TS编译器代表Adam Boduch解决方案返回一个布尔值,所以我找到一个简单的方法来做到这一点:

_.map(columns, item => { return { key: item.key } });

在这种情况下,您还可以添加一些新属性。

P.S。因为我无法发表评论,所以我将在此处添加有关partialRight用法的说明:

_.map(columns, _.partialRight(_.pick, 'key'));

首先如果您调用_.map(array, func) func 将为每个数组元素调用。所以它是平等的:_.map(array, item => func(item))

第二次调用 partialRight 的结果将是一个函数 newFunc ,因此我们可以将Adam的代码表示为:

var newFunc = _.partialRight(_.pick, 'key');
_.map(array, item => newFunc(item));

第三次我们可以将 newFunc 逻辑表示为:

function newFunc(item) {
    return _.pick(item, 'key')
}

最后我认为这个问题最容易理解和最易读的解决方案是:

_.map(columns, item => _.pick(item, 'key'))

答案 2 :(得分:12)

我在map examples中找到了更简单的解决方案:

var users = [
    {name:'David',age:35},
    {name:'Kate',age:28},
];
_.map(users,'age'); // [35,28]

答案 3 :(得分:4)

如果您只想要其中一个键,那么:

_.map(collection, 'key')

你的例子:

var columns = [
    { key:'url', width:20, type:'text' },
    { key:'title', width:21, type:'text' },
    { key:'desc', width:22, type:'text' },
    { key:'domain', width:23, type:'text' },
    { key:'user', width:24, type:'text' }
];

var widths = _.map(columns, 'width');

widths
[20, 21, 22, 23, 24]

答案 4 :(得分:3)

简答

我广泛地研究了 the docs,目前在 Lodash 4.17.15(截至 2021 年 1 月 26 日)中,库中似乎没有直接实现此类功能。 因此:

_.map(columns, row => _.pick(row, 'key'))

//Output, when using the columns collection I introduce below:
//[{key: "url"},
// {key: "user"},
// {}]

应被视为首选解决方案。 如果未找到密钥:

  • 密钥不会直接附加到对象上
  • 你会得到一个空物体

长答案

我认为,替代方法可能存在哪些优点和缺点,可能值得一提。让我们来看看它们之间的关系,考虑以下集合,旨在展示每个解决方案可能出现的一些缺点:

var columns = [
    { 0: 'pos_url', key:'url', width:20, type:'text' },
    { 1: 'pos_user', key:'user', width:24, type:'text' },
    "key"
];

如果您愿意,可以尝试在 Codepen 上解释的所有方法!

这可能有点乱,但我实际上写了一个小代码笔,如果你可以弄脏你的手并尝试这里解释的所有内容,请随意尝试:

https://codepen.io/jhack_jos/pen/gOwVReB

您必须打开浏览器控制台才能看到 console.log 输出。

1) 使用“项目”功能

如果和我一样,你真的不想使用 mixins 和 Lodash 搞混,你也可以使用我创建的以下函数,称为 project。它的灵感来自 Michael Fogus “Functional JavaScript”,因为他在其关于“Table-Like”数据和“Applicative Programming”的演讲中介绍了一个类似的函数。该名称来自关系代数中名为 projection 的一元运算,其作用非常相似。

使用 ES6

function project(...properties)
{
  return function project(object, index, array)
    {
      return _.pick(object, ...properties);
    };
}

没有 ES6

function project(/* properties here */)
{
  var properties = arguments;
  return function project(object, index, array)
    {
      return _.pick.bind(null, object).apply(null, properties);
    };
}

一些用法示例:

_.map(columns, project("key"));

//Output:
//[{key: "url"},
// {key: "user"},
// {}]

_.map(columns, project("key", "width"));

//Output:
//[{key: "url", width: 20},
// {key: "user", width: 24},
// {}]

_.map(columns, project("key", ["width", "type"]));

//Output:
//[{key: "url", width: 20, type: "text"},
// {key: "user", width: 24, type: "text"},
// {}]

project 的不同实现有多快?

  • ES6 实现总是最快的
  • 没有 ES6,在 V8 上,bind + apply 组合是我发现的最快的实现。在 SpiderMonkey 上,即使差距很小,使用 Array.prototype.concat 也更快。您可以在下面的链接中找到此类实现。
  • 链接到 jsbench.me 上的测试,还包含 project 的其他可能实现:https://jsbench.me/2gkkfl9pw4

如果没有找到密钥:

  • 密钥不会直接附加到对象上
  • 你会得到一个空物体

2) _.mapPick 方法

我真的很喜欢@pery mimon 在问题中使用的 mixin 方法。也可以将 _.chain 与它一起使用,这使得代码非常易读。请记住与未来的 Lodash 更新发生冲突的可能性。不幸的是,当将属性列表作为参数抛出时,该算法的行为并不像人们预期的那样(它忽略除第一个参数之外的所有参数)。我可能会建议以下改进:

使用 ES6

_.mixin({
    mapPick:  function (objs, ...keys) {
        return _.map(objs, function (obj) {
            return _.pick(obj, ...keys);
        })
    }
});

没有 ES6

_.mixin({
    mapPick:  function (objs) {
        var properties = arguments;
        return _.map(objs, function (obj) {
            return _.pick.bind(null, obj).apply(null, properties);
        })
    }
});

多亏了这一点,我们现在可以做到:

_.mapPick(columns, "key", "width")

//Output now:
//[{key: "url", width: 20}
// {key: "user", width: 24}
// {}]

//Output of the original mixin from the question:
//[{key: "url"}
// {key: "user"}
// {}]

如果没有找到密钥:

  • 密钥不会直接附加到对象上
  • 你会得到一个空物体

3) _.partialRight 方法

不推荐这种方法,因为如果在边缘情况下错误地应用它可能会显得很棘手。 Lodash docs 声明 _.partialRight 执行以下操作:

<块引用>

此方法类似于 _.partial,只是部分应用了参数 附加到它收到的参数。

这意味着 _.partialRight(func, arg1, arg2) (arg3, arg4) 解析为 func(arg3, arg4, arg1, arg2)

错误的做法

因此,以下代码:

_.map(columns, _.partialRight(_.pick, 'key'));

解析为:

_.map(columns, (row, index, columns) => _.pick(row, index, columns, 'key'));

//Output:
//[{0: "pos_url", key: "url"},
// {1: "pos_user", key: "user"},
// {2: "y"}]

如果没有找到密钥:

  • 密钥不会直接附加到对象上
  • 你会得到一个空物体

正确的做法

因此,您应该使用 _.unary 将传递给 _.pick 的参数限制为只有一个。代码如下:

_.map(columns, _.unary(_.partialRight(_.pick, 'key')));

这解析为:

_.map(columns, (row, index, columns) => _.pick(row, 'key'));

//Output:
//[{key: "url"},
// {key: "user"},
// {}]

如果没有找到密钥:

  • 密钥不会直接附加到对象上
  • 你会得到一个空物体

4) 解构和箭头函数

您可能期望这种方法涵盖所有​​内容并且是普通的,但您可能会发现它在边缘情况下有点不灵活。我们来看看:

columns.map(({key}) => ({key}))

//Output:
//[{key: "url"},
// {key: "user"},
// {key: undefined}]

一些相关事实:

  • 你不能选择数字键
  • 不能选择包含空格的键

如果没有找到密钥:

  • 你会得到一个值为“undefined”的键
  • 你永远不会得到空对象
  • _.keys 仍会返回未找到的密钥

5) 在没有键的情况下提取值

这是一个额外的,不符合问题。正如另一个答案指出的那样,如果您不需要这些键,您可以使用 _.map 快捷方式(以前称为 _.pluck)提取值:

_.map(columns, "key")

//Output:
//["url", "user", undefined]

关于这种做法有很多有趣的事情需要了解,因为它隐式地使用了 _.iteratee 函数,这使我们能够将 _.get 与 { {1}} .

如果没有找到密钥:

  • undefined 将用作值
  • 您将得到一个与原始数组长度相同但填充了“未定义”值的数组

答案 5 :(得分:2)

使用ES6 destructuringarrow functions,您可以这样做:

columns.map(({width}) => ({width}))

不需要lodash。