【问题标题】:underscore where with or condition (underscore, lodash or any other solution)下划线或条件(下划线、lodash 或任何其他解决方案)
【发布时间】:2016-03-26 02:05:22
【问题描述】:

我实现了一个 mixin 来用 _.where 添加“或”条件

var arr = [{a:1,b:4}, {a:5}, {a:6}, {a:11}];
_.mixin({
   or: function(obj,arr,condition){
     return _.chain(arr).where(condition).union(obj).value();
   }
});

现在我可以像这样使用它了,它的工作原理有点像 sql 查询

_.chain(arr).where({a:1}).or(arr,{a:11,b:3}).or(arr,{a:2}).value();
//returns [{a:1,b:4}]

_.chain(arr).where({a:1}).or(arr,{a:11}).or(arr,{a:2}).value();
//returns [{a:1,b:4},{a:11}]

_.chain(arr).where({a:1}).or(arr,{a:11}).or(arr,{b:4}).value();
//returns [{"a":1,"b":4},{"a":11}] --no duplicates

但我想找到一种更好的方法来调用 _.or({a:1}),现在我必须每次都通过 arr _.or(arr,{a:1}),作为链式“或" 获取第一个对象作为先前执行的函数的结果。

有没有办法在链式 mixin 函数中获取整个数组?

我希望下面返回与我上面的实现相同的结果。 (就像一个完美的 sql 查询)

_.chain(arr).where({a:1}).or({a:11,b:3}).or({a:2}).value();//returns [{a:1,b:4}]

我的主要重点是通过下划线来获得它,但实际上任何其他方式,如 lodash 甚至其他一些库或解决方案也可以。此外,我们不需要使用链接,我也在尝试使用 compose,但到目前为止还没有运气。我想要一个在最少行中完美运行的解决方案。鼓励任何其他改进它的建议。

jsFiddle

【问题讨论】:

  • 我不认为扩展下划线是正确的方法。请参阅:您必须在结果中传递初始数组的引用,以便每个or() 能够添加更多从初始集合过滤的后续结果。 Underscore 的链接不是为此而设计的。你可以“弯曲”它,但代价是你想要的短。自己实现一个流畅的界面并不像你想象的那么难——这样做可以吗?如果是这样,请编辑您的问题并删除下划线要求(留下您的示例以显示您尝试过的内容)。
  • well underscore 和 lodash 都允许您通过 mixin 使用您的自定义代码来扩展功能,您是对的,链接不是为此而设计的,我对任何其他概念或机制持开放态度。让我更新我的问题以删除下划线和链式依赖项。
  • 好的,我更新了这个问题,让它对所有可能的解决方案开放。
  • 由于您尝试与链的当前实现“冲突”,您是否尝试过覆盖链本身以始终将根作为最后一个参数传递?
  • 还没有,我实际上不想覆盖链,因为我不确定它会在应用程序的其他部分中断什么。我可以只为这个实例覆盖链吗?

标签: javascript underscore.js lodash


【解决方案1】:

这是一个有趣的问题,感谢 Rahul 提醒我注意这个问题!

我想出了一个使用下划线辅助函数(虽然没有链接)解决这个问题的函数。它需要 2 个参数,即您开始使用的 array,以及带有您想要匹配的属性的 propsObj 数组。

JsFiddle

【讨论】:

    【解决方案2】:

    我不认为扩展下划线是正确的方法,下划线的链接不是为此而设计的。你可以“弯曲”它,但代价是你想要的短。

    链接的工作原理

    Underscore 的链接是一种“流操作”。 chain() 将传递的对象内部存储在闭包中,并返回每个方法都绑定到该对象的下划线接口。应用过滤器函数时,将返回结果并在内部存储在结果对象中。然后value() 调用将返回内部引用。

    自定义解决方案

    您可以使用所谓的流畅界面来解决这个问题:

    // Fluent interface to filter an array with chained, alternative conditions.
    // Usage: whereOr([...]).or({...}).or({...}).end()
    //        whereOr([...], {...}).or({...}).end()
    // Objects are _.where() conditions to subsequently apply to the array.
    function whereOr(arr, condition) {
        var result = [],
            iface;
    
        iface =  {
            or: function(subcondition) {
                result = result.concat(_.where(arr, subcondition));
                return iface;
            },
            end: function() {
                return _.union(result);
            }
        };
    
        if (condition) {
            return iface.or(condition);
        }
    
        return iface;
    }
    

    你可以这样做

    var arr = [{a:1,b:4}, {a:5}, {a:6}, {a:11}];
    whereOr(arr).or({a:1}).or({a:11}).or({a:2}).end();
    

    如果你喜欢,你当然可以把这个功能混入下划线:

    _.mixin({
        whereOr: whereOr
    });
    

    工作原理

    whereOr() 被调用时,它会建立一个包含对原始arr 的引用的范围。它返回一个提供流畅函数or()end() 的对象,它们都可以访问初始数组。虽然end() 将返回结果,但or() 会将Userscore 的where() 应用于内部引用并将结果添加到result。然后它再次返回接口对象。这样or() 提供了另一种过滤的可能性,直到end() 返回联合结果。在此示例中,whereOr() 可选择接受第一个过滤条件作为第二个参数。

    您可以将whereOr()chain()end()value() 进行比较。

    jsFiddle

    【讨论】:

    • 有趣的解决方案,投赞成票,但这个解决方案不起作用jsfiddle.net/6sx435bk/1,因为它每次只选择第一个匹配值。你能在 jsFiddle 中纠正它吗?另外我试图避免每次都传递 arr,你可以尝试只将 arr 传递给 whereOr 函数吗?
    • 确实如此。 Don't call or() with arr as first argument。它的第一个参数是where() 的(另一个)过滤条件。 :)
    • 酷,实际上我从答案中复制了它,你将 arr 传递给每个或调用,你可以更新你的答案并添加 jsFiddle 链接。看起来挺好的。谢谢:)
    • 感谢您在我的回答中修复此(复制和粘贴)错误 - 编辑已获批准。
    【解决方案3】:

    试试这个:

    // makeOr is a constructor for a 'chainable' type with: `{or, value}`
    var makeOr = () =>  {
      var fs = []
      var obj = {
        or: f => {
          fs.push(f)
          return obj
        },
        value: (arr) => _.chain(fs).map(f => _.where(arr, f)).union().flatten().value()
      }     
      return obj
    }
    
    // override underscore where 
    // if f is our 'chainable' type we evaluate it, otherwise we default to underscore where
    (function() {
      where = _.where
      _.mixin({
        where: (arr, f) => (!!f.value && !!f.or) ? f.value(arr) : where(arr, f)
      })
    })();
    
    var result = _.chain(arr)
      .where(makeOr().or({a: 1}).or({a:5}).or({a:11}))
      .where({a:5}).value()
    

    JSFiddle

    PS。也看看这个:Hey Underscore, You're Doing It Wrong!

    【讨论】:

    • 感谢 homam 的链接,它真的很有趣,我知道下划线将第一个参数传递给 mixin 但在我的情况下,“或”函数总是与 _.where 一起使用(类似于 sql 查询)所以我有要使用 _.chain,现在如果我使用链,那么传递给下一个函数的对象是先前执行的链函数的结果。因此,在这种情况下,您的解决方案将不起作用。您可以使用我创建的 jsFiddle 来检查结果
    • 我不完全明白你的意思。这是更新后的小提琴jsfiddle.net/j8zz5qo1,无论是您的原始or 还是我的or1,我们都会得到相同的结果,它的第一个参数是arr
    • 好的,检查一下jsfiddle.net/j8zz5qo1/1 我更新了第一个“或”子句以匹配两个值。在我的结果中,您将获得两个值,但在您的解决方案中,您将仅获得与 where 子句匹配的第一个值。
    猜你喜欢
    • 1970-01-01
    • 2016-10-26
    • 1970-01-01
    • 2018-05-04
    • 1970-01-01
    • 2021-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多