问题 将Node.js中的数组中的类似字符串分组


假设我在数组中有一组不同的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个项目进行比较和分组

我找到了 字符串相似性库 这使得可以将一个字符串与一组字符串进行比较。一种方法是迭代源,将每个项目与源集合进行比较,并应用规则对具有相似分数的项目进行分组。不过,我想这将是非常低效的。

有人可以建议我一个有效的方法来完成我需要的东西吗?


3331
2018-02-13 20:44


起源

所以在这个例子中有一个明确的模式,但似乎你问的字符串可能是什么?那是对的吗? - aw04
@ aw04是的,没有明确的模式字符串可以是任何东西。正如我写的:源中的所有项都可以是随机字符串 - enyce12
那么祝你好运吧 :) - aw04
只是一个注意事项,类似的分数想法太简单了,你只看到一个字符串如何与另一个字符串相关,而不是它们如何相互关联。我唯一想到的就是第一次经历并以某种方式弄清楚你的不同阵列组,但这听起来像一个非常复杂的算法 - aw04
你完全正确,但我认为必须已经存在一种算法来完成这种比较(mb没有在Node.js中实现),我只是不知道。所以我希望有人把我推向正确的方向:) - enyce12


答案:


我能想到的最好的解决方案是将字符串相互比较并测试它们的不同之处。有一种算法可以做到这一点,即 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)。


10
2018-02-17 01:21



尽管我使用相同的算法提出了一个库,但您的解决方案正在运行。我还没有测量过性能,但谢谢你的回答! - enyce12
应该 levenshtein.get(? - John Jones
@JohnJones感谢您的注意。


MinHash怎么样(https://en.wikipedia.org/wiki/MinHash)?

它旨在查找重复的网页。所以我想你可以url.split('/')并将每个url视为一组单词。


1
2018-02-22 23:24



这看起来很有趣。我要深入了解一下,谢谢! - enyce12


你没有充实自己的意图,但如果面临从随机干草堆中找到最近邻居的选定项目的任务,我可能会尝试建立一个哈希树。

或者,这可能是作弊,我让图书馆为我做。 lunr.js 基本上是一个纯粹的JS lucene索引,我会把你的数组推入它并查询它以获得类似的字符串。我以前在lunr.js中有相当大的数据集,并且它具有高性能,附近有一个弹性搜索集群没有蜡烛,但仍然令人印象深刻。

如果你提供一些关于你想要做什么的更多细节,我可以提供一些更多的细节,甚至可能是一些示例代码。


0
2018-02-17 01:20





如果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
}

0
2018-02-21 04:36





根据您的示例测试,我建议您实施一个 基数树或前缀树 存储字符串。之后,您可以定义一个标准来聚类这些字符串。


0
2018-02-23 05:04