如何找到数组中对象的最佳匹配?

时间:2016-10-31 21:52:35

标签: javascript arrays

如果以下代表我的数组,

1 UserA | Bob Smith | 12345 | hello
2 UserA | Bob Smith | 12345 |
3 UserA | Bob Smith | 123   |
4 UserA | Bob Smith
5 UserB | Bob Smith | 12333 | hello

我将以下对象与:

进行比较
UserA | Bob Smith | 2222 

我喜欢"最匹配"在我的用户阵列上的第4行。

UserA | Bob Smith | 2222 | hello也与第4行匹配。

我能做些什么来获得最佳匹配?

我可以循环并做一个if-else,但这似乎很脏,希望有人有一个聪明的解决方案。

我目前的做法:

尝试匹配

UserA | Bob Smith | 2222 | hello

它什么也没有返回,所以切掉最后一个并再试一次

UserA | Bob Smith | 2222

它什么也没有返回,所以切掉最后一个并再试一次

UserA | Bob Smith 

匹配第4行,返回true!思考??

其他信息:

[
{"Title": "UserA", "Name": "Bob Smith", "Number": 1234, "output": "hello"},
{"Title": "UserA", "Name": "Bob Smith", "Number": 1234},
{"Title": "UserA", "Name": "Bob Smith", "Number": 123},
{"Title": "UserA", "Name": "Bob Smith"},
{"Title": "UserA", "Name": "Bob Smith", "Number": 12333, "output": "hello"}
]

它们都包含标题,名称,编号。有些可能包含或不包含其他信息,例如"输出"

我想要的对象

{"Title": "UserA", "Name": "Bob Smith", "Number": 122, "output": "hello"}

将与第4个数组对象匹配,因为它从左到右匹配,这是"最佳匹配"

7 个答案:

答案 0 :(得分:1)

您可以尝试映射一个函数,该函数返回数组中每个项目的分数,然后获得分数最高的索引,而不是每次都循环播放?

var arr = [
{"Title": "UserA", "Name": "Bob Smith", "Number": 1234, "output": "hello"},
{"Title": "UserA", "Name": "Bob Smith", "Number": 1234},
{"Title": "UserA", "Name": "Bob Smith", "Number": 123},
{"Title": "UserA", "Name": "Bob Smith"},
{"Title": "UserA", "Name": "Bob Smith", "Number": 12333, "output": "hello"}
];

var target = {"Title": "UserA", "Name": "Bob Smith", "Number": 122, "output": "hello"};

var highest = 0;
var index = undefined;

function score(obj, el, i) {
  var s = 0;
  Object.keys(obj).forEach(key => s += el[key] === obj[key] ? 1 : 0);
  if (s > highest) {
    highest = s;
    index = i;
  }
}

arr.forEach((el, i) => score(target, el, i));

这应该留下最高等于最高分数和索引等于数组中该项目的索引。

答案 1 :(得分:1)

我的看法

/**
 @func   bestMatch - returns best matching object
 @desc   This function takes an array of objects (haystack) and compares
         each property of needle to the property of haystack[n]. 
         haystack[n] gets a "score" based on how many properties exist and
         match the properties of needle, and js custom sort method is 
         used, based off the score, so that the first element in the 
         sorted haystack should have the highest score and therefore 
         "win" and be the best match 
 @param1 Array of objects to match against (haystack)
 @param2 Object to find matches for (needle)
 @return Object from haystack that is closest match against needle
 **/
function bestMatch(h,n) {
  return h.sort(function(a,b){
    var c=0,d=0,p;
    for (p in n) {
      if (n.hasOwnProperty(p)) {
        c+=Number((a[p]||0)&&a[p]===n[p]);
        d+=Number((b[p]||0)&&b[p]===n[p]);
      }  
    }
    return (d<c)?-1:1;return 0;
  })[0];
}

示例

var data = [
  {"Title": "UserA", "Name": "Bob Smith", "Number": 1234, "output": "hello"},
  {"Title": "UserA", "Name": "Bob Smith", "Number": 1234},
  {"Title": "UserA", "Name": "Bob Smith", "Number": 123},
  {"Title": "UserA", "Name": "Bob Smith", "Number": 12333, "output": "hello"},
  {"Title": "UserA", "Name": "Bob Smith"}
];

var input=  {"Title": "UserA", "Name": "Bob Smith", "Number": 12333};

bestMatch(data,input); 
// return: {"Title":"UserA","Name":"Bob Smith","Number":12333,"output":"hello"}

答案 2 :(得分:0)

线性搜索:

<script>

function FindBestMatch( a, item )
{
    var bestIndex = null;
    var bestNumMatch = -1;
    var bestNumMismatch = 0;
    var numItemElements = item.length;
    for (var i in a)
    {
        for (var numMatch=0; numMatch<numItemElements; numMatch++)
        {
            if (a[i][numMatch]!=item[numMatch]) break;
        }
        var numMismatch = a[i].length - numMatch;
        if (numMatch==numItemElements && !numMismatch) return i;
        if (numMatch>bestNumMatch
          || (numMatch==bestNumMatch && numMismatch<bestNumMismatch))
        {
            bestIndex = i;
            bestNumMatch = numMatch;
            bestNumMismatch = numMismatch;
        }
    }
    return bestIndex;
}

var myArray = [
[ 'UserA', 'Bob Smith', 12345, 'hello' ],
[ 'UserA', 'Bob Smith', 12345 ],
[ 'UserA', 'Bob Smith', 123 ],
[ 'UserA', 'Bob Smith' ],
[ 'UserA', 'Bob Smith', 12345, 'hello' ]
];

var someItem = [ 'UserA', 'Bob Smith', 2222 ];

var i = FindBestMatch(myArray,someItem);

alert("The best match is number "+i+":\n\n"+myArray[i].join(" | "));

</script>

答案 3 :(得分:0)

这是一个这样做的例子。它构建简单的匹配索引,然后按相关性按降序排序(第一个是最好的)。代码:

function searchByRelevance(search, data) {
    let props = Object.getOwnPropertyNames(search);

    return data.map((value) => { // Build match index
      let match = 0;
      for(let prop of props) {
        if (value[prop] !== search[prop]) {
          break;
        }

        match++;
      }

      return {
        value,
        match,
        tail: match
          ? Object.getOwnPropertyNames(value).length - match
          : Infinity,
      };
    })
    .filter((item) => item.match) // Filter matched items only
    .sort((a, b) => { // Sort by relevance
      if (a.match !== b.match) {
        return -(a.match - b.match);
      }

      if (a.tail !== b.tail) {
        return a.tail - b.tail;
      }

      return 0;
    })
    .map((item) => item.value) // Get values from index items
    ;
}

// Example with time test

console.time('generate');
let set = [
  {"Title": "UserA", "Name": "Bob Smith", "Number": 1234, "output": "hello"},
  {"Title": "UserA", "Name": "Bob Smith", "Number": 1234},
  {"Title": "UserA", "Name": "Bob Smith", "Number": 123},
  {"Title": "UserA", "Name": "Bob Smith"},
  {"Title": "UserA", "Name": "Bob Smith", "Number": 12333, "output": "hello"}
];

let data = [];

for(let i = 0; i < 10000; i++) {
  data = [...data, ...set];
}

console.timeEnd('generate');

let search = {"Title": "UserA", "Name": "Bob Smith", "Number": 1234, "output": "hello"};

console.time('search');

let matches = searchByRelevance(search, data);

console.timeEnd('search');

console.log(matches); // Matched items sorted by relevance

在你的例子中搜索50K文档的集合需要大约17毫秒。

答案 4 :(得分:0)

通过对象属性从左向右移动是不可靠的,因为不保证以特定顺序返回对象属性。

您可以将分数分配给数据集中某个对象(大海捞针)可能出现的四种情况,与您想要找到(针)的匹配对象相比:

  • 针头上有一个大海捞针没有的钥匙;
  • 大海捞针中的一行有一根针没有;
  • 他们共享一个密钥,但相应的值是不同的;
  • 他们共享一个密钥,相应的值是相同的

如果您为这四个中的每一个分配一个分数,您可以为两个中的任何一个(大海捞针和针)中的所有属性添加分数:这将为该特定行提供分数。

然后选择最佳匹配行,即得分最高的行。

&#13;
&#13;
function bestMatch(needle, haystack) {
    return haystack
        .map( row => 
            [...new Set(Object.keys(needle).concat(Object.keys(row)))]
                .reduce( (score, key, i) => 
                    score + (!(key in needle)         ? -10 
                           : !(key in row   )         ? - 5
                           : needle[key] !== row[key] ? -30 
                           :                              5), 0) )
        .reduce((best, score, i) => score > best[0] ? [score, i] : best, [-9999, i])
        .pop();
}

// Sample data
var haystack = [
    {"Title": "UserA", "Name": "Bob Smith", "Number": 1234, "output": "hello"},
    {"Title": "UserA", "Name": "Bob Smith", "Number": 1234},
    {"Title": "UserA", "Name": "Bob Smith", "Number": 123},
    {"Title": "UserA", "Name": "Bob Smith"},
    {"Title": "UserA", "Name": "Bob Smith", "Number": 12333, "output": "hello"}
];
var needle = {"Title": "UserA", "Name": "Bob Smith", "Number": 122,"output": "hello"};
// Get best match
var i = bestMatch(needle, haystack);
// Show results
console.log('best match at index ', i);
console.log(haystack[i]);
&#13;
&#13;
&#13;

您可以在代码中看到上述4种情况的4个分数。你可以根据自己的喜好调整这些。您甚至可以根据属性的名称给出不同的分数,以便&#34; Title&#34;会得到更多的分数,等于&#34;数字&#34;值。

答案 5 :(得分:0)

如果我理解正确,那么您正在尝试制定匹配评估标准,其中未定义的值优于不同的值,以便
{{Title“:”UserA“,”Name“:”Bob Smith“,”Number“:2222}
更接近
{{Title“:”UserA“,”Name“:”Bob Smith“}

{{Title“:”UserA“,”Name“:”Bob Smith“,”Number“:123}
我建议将匹配计为正数,将不同值计为负值,将未定义计为中性:

function bestMatch(array, object) {
  if (!array) {array = [];}
  if (!object) {object = {};}
  var best_index = -1, best_value = 0;
  for (var i = 0; i < array.length; i++) {
    if (array[i]) {
      var matches = 0, total = 0;
      for (var p in object) {
        total++;
        if (array[i][p]) {
          if (array[i][p] == object[p]) {
            matches++;
          } else {
            matches--;
          }
        }
      }
      var value = matches/total;
      if (value > best_value) {
        best_value = value;
        best_index = i;
      }
    }
  }
  return best_index;
}

var bestIndex = bestMatch(
  [
    {"Title": "UserA", "Name": "Bob Smith", "Number": 1234, "output": "hello"},
    {"Title": "UserA", "Name": "Bob Smith", "Number": 1234},
    {"Title": "UserA", "Name": "Bob Smith", "Number": 123},
    {"Title": "UserA", "Name": "Bob Smith"},
    {"Title": "UserA", "Name": "Bob Smith", "Number": 12333, "output": "hello"}
  ],
  {"Title": "UserA", "Name": "Bob Smith", "Number": 2222}
);

console.log(bestIndex); // 3

答案 6 :(得分:-1)

如果您可以将对象重组为

[
{item:  UserA | Bob Smith | 12345 | hello},
{item:  UserA | Bob Smith | 12345 | },
{item:  UserA | Bob Smith | 123   |},
{item:  UserA | Bob Smith},
{item:  UserB | Bob Smith | 12333 | hello},
]

您可以使用.filter()函数来获取匹配的记录,例如

Array.prototype.filteredItem = function(key, value) {
    return this.filter(function(f) { return f[key] === value; })
}

然后您可以将filteredItem用作:

var result =myArr.filteredItem("item", "UserB | Bob Smith | 12333 | hello");