【问题标题】:Fuzzy String search but for objects?模糊字符串搜索但对象?
【发布时间】:2020-07-02 08:05:54
【问题描述】:

有很多库提供模糊字符串搜索功能,您可以在其中提供输入字符串,然后搜索“足够接近”的字符串。

例如,如果您输入单词cat,您可能会得到:

  • 猫 (100%)
  • 帽子
  • 老鼠

除了对象之外,还有什么功能吗?一种算法,它采用两个对象并比较不是它们是否相似(这很简单并且已经完成),而是而是比较它们的相似程度。相似程度可以是从 0 到 100% 的百分比,其中 100% 是完全匹配,而 0% 根本不相似,或者它可以是一个数字,例如 https://en.wikipedia.org/wiki/Levenshtein_distance,其中 0 是完全匹配,而number 将是“接近度”,没有上限。

例如,如果我有一个对象(javascript):

const a = {
  name: 'John Doe',
  age: 18
}

如果我将它与另一个对象进行比较:

const b = {
  name: 'Some random name',
  age: 5
}

你会得到一些不是 0 的分数。我不确定你会得到什么值,这取决于你将使用什么评分系统(0-100% 或 x >= 0)

但是,与将 a 与 b 进行比较相比,将其与完全不同的事物进行比较,例如:

const c = {
  foo: 0
}

这里的对象几乎完全不相似。所以这里的分数会更差(对于 0-100%,它会是一个较小的数字,对于 x>=0,它会是一个更高的数字,而不是 a 与 b 相比)。

是否有任何现有的算法或库可以实现这样的目标?特别是在javascript中?

我发现了一些可能与此类似的库,但我添加了批评:

【问题讨论】:

    标签: javascript algorithm fuzzy-search


    【解决方案1】:

    如果对象最终由可序列化的值组成(没有类实例或函数),一种选择是将它们字符串化,然后对这些字符串使用相同类型的“模糊搜索”算法:

    const a = {
      name: 'John Doe',
      age: 18
    };
    const b = {
      name: 'Some random name',
      age: 5
    };
    const c = {
      foo: 0
    };
    
    // https://gist.github.com/andrei-m/982927
    const getEditDistance = ((t,n)=>{if(0==t.length)return n.length;if(0==n.length)return t.length;var h,e,r=[];for(h=0;h<=n.length;h++)r[h]=[h];for(e=0;e<=t.length;e++)r[0][e]=e;for(h=1;h<=n.length;h++)for(e=1;e<=t.length;e++)n.charAt(h-1)==t.charAt(e-1)?r[h][e]=r[h-1][e-1]:r[h][e]=Math.min(r[h-1][e-1]+1,Math.min(r[h][e-1]+1,r[h-1][e]+1));return r[n.length][t.length]});
    const diff = (obj1, obj2) => {
      const str1 = JSON.stringify(obj1);
      const str2 = JSON.stringify(obj2);
      const dist = getEditDistance(str1, str2);
      const peakDist = Math.max(str1.length, str2.length);
      return (peakDist - dist) / peakDist;
    }
    console.log(diff(b, b));
    console.log(diff(b, a));
    console.log(diff(b, c));

    如果您只想比较对象形状,而不是基础值,您可以在调用JSON.stringify 时使用替换函数将所有原始值替换为空字符串。例如,您的ab 对象都将变为{"name":"","age":""},但您的c 对象将变为{"foo":""}

    const a = {
      name: 'John Doe',
      age: 18
    };
    const b = {
      name: 'Some random name',
      age: 5
    };
    const c = {
      foo: 0
    };
    
    // https://gist.github.com/andrei-m/982927
    const getEditDistance = ((t,n)=>{if(0==t.length)return n.length;if(0==n.length)return t.length;var h,e,r=[];for(h=0;h<=n.length;h++)r[h]=[h];for(e=0;e<=t.length;e++)r[0][e]=e;for(h=1;h<=n.length;h++)for(e=1;e<=t.length;e++)n.charAt(h-1)==t.charAt(e-1)?r[h][e]=r[h-1][e-1]:r[h][e]=Math.min(r[h-1][e-1]+1,Math.min(r[h][e-1]+1,r[h-1][e]+1));return r[n.length][t.length]});
    const stringify = obj => JSON.stringify(obj, (key, val) => (
      typeof val === 'object' && val !== null
        ? val
        : ''
    ));
    const diff = (obj1, obj2) => {
      const str1 = stringify(obj1);
      const str2 = stringify(obj2);
      const dist = getEditDistance(str1, str2);
      const peakDist = Math.max(str1.length, str2.length);
      return (peakDist - dist) / peakDist;
    }
    console.log(diff(b, b));
    console.log(diff(b, a));
    console.log(diff(b, c));

    【讨论】:

    • 我认为对于我的用例,这应该足够作为一个起点。我打算使用一些函数,尤其是相对距离,并制作一个自定义函数,使用 levenshtein 距离来比较相同的字段。
    猜你喜欢
    • 1970-01-01
    • 2017-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-23
    • 2021-09-13
    • 2010-09-24
    • 1970-01-01
    相关资源
    最近更新 更多