假设我在数组中有一组不同的URL:
var source = ['www.xyz.com/Product/1', 'www.xyz.com/Product/3', 'www.xyz.com/Category/1', 'somestring']
什么是迭代数组并将类似字符串分组到单独数组中的好方法?
上面示例中的所需输出将是:
var output = [
['www.xyz.com/Product/1', 'www.xyz.com/Product/3'],
['www.xyz.com/Category/1'],
['somestring']
];
条件
- 所有项目
source
可以是随机字符串
- 逻辑必须能够在有意义的时间内对100'000个项目进行比较和分组
我找到了 字符串相似性库 这使得可以将一个字符串与一组字符串进行比较。一种方法是迭代源,将每个项目与源集合进行比较,并应用规则对具有相似分数的项目进行分组。不过,我想这将是非常低效的。
有人可以建议我一个有效的方法来完成我需要的东西吗?
我能想到的最好的解决方案是将字符串相互比较并测试它们的不同之处。有一种算法可以做到这一点,即 Levenshtein距离 算法:
Levenshtein距离是用于测量的距离的字符串度量
两个序列之间的差异。非正式地,Levenshtein距离
两个单词之间是单字符编辑的最小数量
(即插入,删除或替换)需要更改一个
说到另一个。
我们可以轻松地创建一个Levenshtein过滤器 fast-levenshtein模块:
const levenshtein = require('fast-levenshtein');
const levenshteinFilter = (source, maximum = 5) => {
let _source, matches, x, y;
_source = source.slice();
matches = [];
for (x = _source.length - 1; x >= 0; x--) {
let output = _source.splice(x, 1);
for (y = _source.length - 1; y >= 0; y--) {
if (levenshtein.get(output[0], _source[y]) <= maximum) {
output.push(_source[y]);
_source.splice(y, 1);
x--;
}
}
matches.push(output);
}
return matches;
}
let source = ['www.xyz.com/Product/1', 'www.xyz.com/Product/3', 'www.xyz.com/Category/1', 'somestring'];
let output = levenshteinFilter(source);
// [ [ 'www.xyz.com/Product/1', 'www.xyz.com/Product/3' ],
// [ 'www.xyz.com/Category/1' ],
// [ 'somestring' ] ]
您可以在函数的2参数中定义最大可接受距离(默认为5)。
MinHash怎么样(https://en.wikipedia.org/wiki/MinHash)?
它旨在查找重复的网页。所以我想你可以url.split('/')并将每个url视为一组单词。
你没有充实自己的意图,但如果面临从随机干草堆中找到最近邻居的选定项目的任务,我可能会尝试建立一个哈希树。
或者,这可能是作弊,我让图书馆为我做。 lunr.js 基本上是一个纯粹的JS lucene索引,我会把你的数组推入它并查询它以获得类似的字符串。我以前在lunr.js中有相当大的数据集,并且它具有高性能,附近有一个弹性搜索集群没有蜡烛,但仍然令人印象深刻。
如果你提供一些关于你想要做什么的更多细节,我可以提供一些更多的细节,甚至可能是一些示例代码。
如果source包含所有随机URL,则函数下方将给出预期输出,如问题所示。
function filter (source) {
var output = []
source.forEach((svalue) => {
if (output.length === 0) {
output.push([svalue])
} else {
var done = false
output.forEach((tarr) => {
if (!done) {
tarr.forEach((tvalue) => {
if (svalue.indexOf('/') > -1 && svalue.split('/').slice(0, 2).join('/') == tvalue.split('/').slice(0, 2).join('/')) {
tarr.push(svalue)
done = true
}
})
}
})
if (!done) {
output.push([svalue])
done = true
}
}
})
return output
}
根据您的示例测试,我建议您实施一个 基数树或前缀树 存储字符串。之后,您可以定义一个标准来聚类这些字符串。