我正在尝试找出哪一种是交叉一组文本并在其中找到常用词的最佳方式。鉴于这种情况:
var t1 = 'My name is Mary-Ann, and I come from Kansas!';
var t2 = 'John, meet Mary, she comes from far away';
var t3 = 'Hi Mary-Ann, come here, nice to meet you!';
交集结果应该是:
var result =["Mary"];
它应该能够忽略标点符号,如 .,!?-
使用正则表达式的解决方案是否是最优的?
最佳答案
这是一个经过测试的解决方案:
function intersect() {
var set = {};
[].forEach.call(arguments, function(a,i){
var tokens = a.match(/\w+/g);
if (!i) {
tokens.forEach(function(t){ set[t]=1 });
} else {
for (var k in set){
if (tokens.indexOf(k)<0) delete set[k];
}
}
});
return Object.keys(set);
}
这个函数是可变的,你可以用任意数量的文本调用它:
console.log(intersect(t1, t2, t3)) // -> ["Mary"]
console.log(intersect(t1, t2)) // -> ["Mary", "from"]
console.log(intersect()) // -> []
如果您需要支持非英语语言,那么这个正则表达式将不够用,因为 JavaScript 正则表达式对 Unicode 的支持很差。您要么使用 regex library或者您通过显式排除字符来定义正则表达式,如 a.match(/[^\s\-.,!?]+/g);
(这对你来说可能就足够了)。
详细解释:
想法是用第一个文本的标记填充一个集合,然后从该集合中删除其他文本中缺少的标记。
- 集合是用作 map 的 JavaScript 对象。一些纯粹主义者会使用
Object.create(null)
为了避免原型(prototype),我喜欢{}
的简单性. - 因为我希望我的函数是 variadic , 我用
arguments
而不是将传递的文本定义为显式参数。 -
arguments
不是一个真正的数组,所以要迭代它你需要一个for
循环或类似[].forEach.call
的技巧.它起作用是因为arguments
是"array-like" . - 要标记化,我只需使用
match
匹配单词,这里没有什么特别的(不过请参阅上面关于更好地支持其他语言的注释) - 我使用
!i
检查它是否是第一个文本。在这种情况下,我只需将标记复制为集合中的属性。一定要用一个值,我用1
.以后,ES6 sets将使此处的意图更加明显。 - 对于以下文本,我遍历集合的元素(键)并删除不在标记数组 (
tokens.indexOf(k)<0
) 中的元素 - 最后,我返回集合的元素,因为我们需要一个数组。最简单的解决方案是使用
Object.keys
.
关于javascript - 交叉文本以找到常用词,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23905996/