【问题标题】:Filtering big array of Objects with Ramda (or other functional library) using indexOf?使用 indexOf 用 Ramda(或其他功能库)过滤大量对象?
【发布时间】:2017-10-14 02:49:07
【问题描述】:

我喜欢 Ramda,但有时我会在看似简单的事情上绊倒。 我所拥有的是我需要查看的地点和单词列表(它们的 indexOf 必须等于 0)以生成搜索建议列表,例如,鉴于下面的列表,我想向用户建议当他们在搜索框中输入“aber”时,所有 3 个区域(按名称),但如果他们输入“nor”,则只有 Aberdeen North。

[
  {
    name: "Aberavon",
    words: [ "aberavon" ]
  },
  {
    name: "Aberconwy",
    words: [ "aberconwy" ]
  },
  {
    name: "Aberdeen North",
    words: [ "aberdeen", "north" ]
  },
]

我设法让“正确”的东西运行,通过控制台日志获取我的结果,但是我似乎没有得到过滤的项目。我的代码看起来像这样,控制台日志将正确显示遍历单词的迭代和匹配术语开头的 indexOf,但我永远不会将我的对象从过滤器中取出。我试图让它更实用,甚至更无功能,到目前为止没有结果!这是我的代码:

var matches = R.filter ( function ( x ) { R.map ( function ( word ) {
        console.log ( word );
        console.log ( word.indexOf ( term ) === 0 );
        R.equals ( word.indexOf ( term ), 0 )
    } ) ( x.words ) } ) ( json );

【问题讨论】:

    标签: javascript arrays filter functional-programming ramda.js


    【解决方案1】:

    不是 Ramda 专家,但我可能可以提供帮助。

    您传递给R.filter 的函数(第一个参数)应该采用object 并返回bool

    关键是以可读和可重用的方式创建此方法。最终,您会得到如下结果:

    • 从对象{} 转到字符串数组[string]
    • 从字符串数组[string] 转换为布尔值bool

    第 1 步:检查 word 是否与 term 匹配

    目前,您已经定义了一个基本上是您的“开始于”测试的函数:

    R.equals ( word.indexOf ( term ), 0 )
    

    此函数需要两个字符串才能工作:wordterm(从技术上讲,它们可以是实现 indexOf 的任何东西,但让我们继续举例)

    我会首先测试这个方法并给它一个名字,所以你知道这部分已经“完成”并且可以工作了。

    const startsWith = term => word => word.indexOf(term) === 0;
    

    (稍后,您可以重写它以使用 Ramda API 并包含其他功能,例如区分大小写。您可能还想注释 string -> string -> bool 之类的东西,但同样,我不知道 Ramda 方式)

    第 2 步:检查 some 字符串是否匹配 term

    现在您可以检查字符串是否与某个词匹配,您需要确定字符串数组中的至少一个字符串是否与某个词匹配。

    在纯javascript中:

    const anyStartsWith = term => arr => arr.some(startsWith(term));
    

    我认为 Ramda 等价物是 R.any:

    const anyStartsWith = term => R.any(startsWith(term));
    

    再一次,测试这个方法,看看它是否像你想要的那样运行。

    第 3 步:检查 Place 是否与术语匹配

    这是最复杂的一步。我们需要从带有words 属性的object 转到我们之前定义的过滤器方法可以处理的东西。

    const placeMatchesTerm = term => place =>
      anyStartsWith(term) (place.words);
    

    第 4 步:过滤

    现在,我们有一个函数,它接受一个术语并返回一个接受Place 的函数。这个,我们可以用来直接过滤我们的地点数组:

    const myPlaces = [ /* ... */ ];
    const filter = (term, arr) => 
      arr.filter(placeMatchesTerm(term));
    
    const aber = filter("aber", myPlaces);
    const nor = filter("nor", myPlaces);
    

    在一个工作示例中(没有 Ramda)

    // string -> string -> bool
    const startsWith = term => word => word.indexOf(term) === 0;
    // string -> [string] -> bool
    const anyStartsWith = term => arr => arr.some(startsWith(term));
    // string -> {} -> bool
    const placeMatchesTerm = term => place => anyStartsWith(term) (place.words);
    // string -> [{}] -> bool
    const filter = term => arr => 
      arr.filter(placeMatchesTerm(term));
    
    const aber = filter("aber")(getPlaces());
    const nor = filter("nor")(getPlaces());
    
    console.log("'Aber' matches", aber.map(p => p.name));
    console.log("'Nor' matches", nor.map(p => p.name));
    
    // Data
    function getPlaces() {
      return [{name:"Aberavon",words:["aberavon"]},{name:"Aberconwy",words:["aberconwy"]},{name:"Aberdeen North",words:["aberdeen","north"]}];
    }

    在 Ramda 中,它可能类似于 this,但同样,我不是专家。我会将 Ramda 标签添加到您的问题中,这使得很可能有人会出现向您展示 Ramda 方式:)

    【讨论】:

    • 很抱歉我不得不接受另一个答案,因为两者都很棒!在发布我的问题后不久,我意识到我的问题是什么,因为封装所有步骤的方式无法返回整个项目,因为“开始”测试处于最深层次。从那以后,我以另一种方式对其进行了修复,但是您记录您的回复的方式确实会在将来帮助我处理其他事情。赞!!!
    【解决方案2】:

    我非常喜欢前面的答案,但是如果你想看看 Ramda 人是如何做到的,这将是我的解决方案:

    const startsWith = R.curry((term, word) => word.indexOf(term) === 0);
    const anyStartsWith = R.curry((term, words) => R.any(startsWith(term), words));
    const anyWordMatches = R.curry(
      (term, place) => anyStartsWith(term, R.prop('words', place))
    );
    const anyPlaceMatches = R.curry(
      (term, places) => R.filter(anyWordMatches(term), places)
    );
    
    anyPlaceMatches('nor', json); 
    //=> [ <Aberdeen North> ]
    anyPlaceMatches('aber', json); 
    //=>[ <Aberavon>, <Abercrombie>, <Aberdeen North> ]
    

    虽然所有这一切都可能是无意义的,但我看不出有什么意义。

    您可以在 Ramda REPL 中看到这一切。

    【讨论】:

    • 太棒了!非常感谢,将不得不剖析这个,但它看起来很棒!
    猜你喜欢
    • 2015-10-20
    • 2019-10-30
    • 2020-06-28
    • 2015-06-19
    • 1970-01-01
    • 2017-09-21
    • 1970-01-01
    • 2017-09-06
    • 1970-01-01
    相关资源
    最近更新 更多