使用indexOf过滤使用Ramda(或其他函数库)的大型对象?

时间:2017-05-15 10:42:42

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

我喜欢拉姆达,但有时我会偶然发现看似简单的事情。 我所拥有的是我需要查看的地方和单词列表(它们的indexOf必须等于0)以生成搜索建议列表,例如,给定下面的列表,我想向用户建议所有3个区域(按名称)在搜索框中键入“aber”但如果键入“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 );

2 个答案:

答案 0 :(得分:3)

不是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并包含其他功能,例如case(in)灵敏度。您可能还想注释类似string -> string -> bool的内容,但是我不知道Ramda方式)

第2步:检查某些字符串是否与term匹配

现在您可以检查字符串是否与术语匹配,您需要查明字符串数组中的至少一个字符串是否与术语匹配。

简单的javascript:

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

我认为Ramda等价物是R.any

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

再次,测试此方法,看看它是否像您希望的那样执行。

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

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

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"]}];
}

在拉姆达,它可能像this,但同样,我不是专家。我会在你的问题中添加Ramda标签,这很可能会有人出现给你看Ramda的方式:)

答案 1 :(得分:0)

我非常喜欢上一个答案,但是如果你想看看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 中看到所有这些内容。