使用 indexOf 使用 Ramda(或其他功能库)过滤大量对象?
Filtering big array of Objects with Ramda (or other functional library) using indexOf?
我喜欢 Ramda,但有时我会在看似简单的事情上犯错误。
我所拥有的是我需要查看的地点和单词列表(它们的 indexOf 必须等于 0)以生成搜索建议列表,例如,给出下面的列表,我想向用户建议当他们在搜索框中键入 'aber' 时,所有 3 个区域(按名称),但如果他们键入 'nor',则仅限 Aberdeen North。
[
{
name: "Aberavon",
words: [ "aberavon" ]
},
{
name: "Aberconwy",
words: [ "aberconwy" ]
},
{
name: "Aberdeen North",
words: [ "aberdeen", "north" ]
},
]
我设法得到 'proper' 东西 运行 通过控制台日志得到我的结果,但是我似乎没有得到过滤的项目。我的代码看起来像这样,控制台日志将正确显示遍历单词的迭代和匹配术语开头的 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 );
不是 Ramda 专家,但我也许可以提供帮助。
您传递给 R.filter
的函数(第一个参数)应该采用 object
和 return 一个 bool
.
关键是以可读和可重用的方式创建此方法。最终,你会得到类似这样的结果:
- 从对象
{}
到字符串数组 [string]
- 从字符串数组
[string]
到布尔值 bool
第 1 步:检查 word
是否与 term
匹配
目前,您已经定义了一个基本上是您的 "starts with" 测试的函数:
R.equals ( word.indexOf ( term ), 0 )
此函数需要两个字符串才能工作:word
和 term
。 (从技术上讲,它们可以是实现 indexOf
的任何东西,但让我们坚持这个例子)
我先测试这个方法并给它起个名字,这样你就知道这部分是 "done" 并且有效。
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
是否与字词匹配
这是最复杂的一步。我们需要从 object
到 words
属性 到我们之前定义的过滤器方法可以处理的东西。
const placeMatchesTerm = term => place =>
anyStartsWith(term) (place.words);
第 4 步:过滤
现在,我们有一个接受项的函数和 return 一个接受 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 的方法:)
我很喜欢前面的回答,但是如果你想看看 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.
中看到所有这些
我喜欢 Ramda,但有时我会在看似简单的事情上犯错误。 我所拥有的是我需要查看的地点和单词列表(它们的 indexOf 必须等于 0)以生成搜索建议列表,例如,给出下面的列表,我想向用户建议当他们在搜索框中键入 'aber' 时,所有 3 个区域(按名称),但如果他们键入 'nor',则仅限 Aberdeen North。
[
{
name: "Aberavon",
words: [ "aberavon" ]
},
{
name: "Aberconwy",
words: [ "aberconwy" ]
},
{
name: "Aberdeen North",
words: [ "aberdeen", "north" ]
},
]
我设法得到 'proper' 东西 运行 通过控制台日志得到我的结果,但是我似乎没有得到过滤的项目。我的代码看起来像这样,控制台日志将正确显示遍历单词的迭代和匹配术语开头的 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 );
不是 Ramda 专家,但我也许可以提供帮助。
您传递给 R.filter
的函数(第一个参数)应该采用 object
和 return 一个 bool
.
关键是以可读和可重用的方式创建此方法。最终,你会得到类似这样的结果:
- 从对象
{}
到字符串数组[string]
- 从字符串数组
[string]
到布尔值bool
第 1 步:检查 word
是否与 term
匹配
目前,您已经定义了一个基本上是您的 "starts with" 测试的函数:
R.equals ( word.indexOf ( term ), 0 )
此函数需要两个字符串才能工作:word
和 term
。 (从技术上讲,它们可以是实现 indexOf
的任何东西,但让我们坚持这个例子)
我先测试这个方法并给它起个名字,这样你就知道这部分是 "done" 并且有效。
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
是否与字词匹配
这是最复杂的一步。我们需要从 object
到 words
属性 到我们之前定义的过滤器方法可以处理的东西。
const placeMatchesTerm = term => place =>
anyStartsWith(term) (place.words);
第 4 步:过滤
现在,我们有一个接受项的函数和 return 一个接受 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 的方法:)
我很喜欢前面的回答,但是如果你想看看 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.
中看到所有这些