简体   繁体   English

Backbone.js - 根据包含多个关键字的Array过滤集合

[英]Backbone.js - Filter a Collection based on an Array containing multiple keywords

I'm using Backbone.js/Underscore.js to render a HTML table which filters as you type into a textbox. 我正在使用Backbone.js/Underscore.js来呈现一个HTML表格,当您键入文本框时会对其进行过滤。 In this case it's a basic telephone directory. 在这种情况下,它是一个基本的电话簿。
The content for the table comes from a Collection populated by a JSON file. 该表的内容来自一个由JSON文件填充的Collection。 A basic example of the JSON file is below: 下面是JSON文件的基本示例:

[{
    "Name":"Sales and Services",
    "Department":"Small Business",
    "Extension":"45446",
},
{
    "Name":"Technical Support",
    "Department":"Small Business",
    "Extension":"18800",
},
{
    "Name":"Research and Development",
    "Department":"Mid Market",
    "Extension":"75752",
}]

I convert the text box value to lower case and then pass it's value along with the Collection to this function, I then assign the returned value to a new Collection and use that to re-render the page. 我将文本框值转换为小写,然后将其值与Collection一起传递给此函数,然后将返回的值分配给新的Collection并使用它重新呈现页面。

filterTable = function(collection, filterValue) {
    var filteredCollection;
    if (filterValue === "") {
        return collection.toJSON();
    }
    return filteredCollection = collection.filter(function(data) {
        return _.some(_.values(data.toJSON()), function(value) {
            value = (!isNaN(value) ? value.toString() : value.toLowerCase());
            return value.indexOf(filterValue) >= 0;
        });
    });
};

The trouble is that the function is literal. 问题是函数是字面的。 To find the "Sales and Services" department from my example I'd have to type exactly that, or maybe just "Sales" or "Services". 要从我的示例中找到“销售和服务”部门,我必须准确输入,或者只是“销售”或“服务”。 I couldn't type "sal serv" and still find it which is what I want to be able to do. 我无法输入“sal serv”,仍然发现它是我想要做的。

I've already written some javascript that seems pretty reliable at dividing up the text into an array of Words (now updated to code in use). 我已经编写了一些javascript,它似乎非常可靠地将文本分成一个单词数组(现在更新为使用中的代码)。

toWords = function(text) {
    text = text.toLowerCase();
    text = text.replace(/[^A-Za-z_0-9@.]/g, ' ');
    text = text.replace(/[\s]+/g, ' ').replace(/\s\s*$/, '');
    text = text.split(new RegExp("\\s+"));
    var newsplit = [];
    for (var index in text) {
        if (text[index]) {
            newsplit.push(text[index]);
        };
    };
    text = newsplit;
    return text;
};

I want to loop through each word in the "split" array and check to see if each word exists in one of the key/values. 我想循环遍历“split”数组中的每个单词,并检查每个单词是否存在于其中一个键/值中。 As long as all words exist then it would pass the truth iterator and get added to the Collection and rendered in the table. 只要存在所有单词,它就会传递真值迭代器并添加到Collection中并在表中呈现。

So in my example if I typed "sal serv" it would find that both of those strings exist within the Name of the first item and it would be returned. 所以在我的例子中,如果我输入“sal serv”,它会发现这两个字符串都存在于第一个项目的Name中,并且会返回它。
However if I typed "sales business" this would not be returned as although both the values do appear in that item, the same two words do not exist in the Name section. 但是,如果我键入“销售业务”,则不会返回,因为尽管两个值都出现在该项中,但“名称”部分中不存在相同的两个单词。

I'm just not sure how to write this in Backbone/Underscore, or even if this is the best way to do it. 我只是不确定如何在Backbone / Underscore中写这个,或者即使这是最好的方法。 I looked at the documentation and wasn't sure what function would be easiest. 我查看了文档并且不确定哪个函数最简单。

I hope this makes sense. 我希望这是有道理的。 I'm a little new to Javascript and I realise I've dived into the deep-end but learning is the fun part ;-) 我对Javascript有点新鲜,我意识到我已经潜入了深渊,但学习是有趣的部分;-)
I can provide more code or maybe a JSFiddle if needed. 如果需要,我可以提供更多代码或JSFiddle。

Using underscore's any and all make this relatively easy. 使用下划线的anyall使这相对容易。 Here's the gist of it: 这是它的要点:

  var toWords = function(text) {
    //Do any fancy cleanup and split to words
    //I'm just doing a simple split by spaces.
    return text.toLowerCase().split(/\s+/);
  };

  var partialMatch = function(original, fragment) {   
    //get the words of each input string
    var origWords = toWords(original + ""), //force to string
        fragWords = toWords(fragment);

    //if all words in the fragment match any of the original words, 
    //returns true, otherwise false
    return _.all(fragWords, function(frag) {
      return _.any(origWords, function(orig) {
        return orig && orig.indexOf(frag) >= 0;
      });
    });
  };

  //here's your original filterTable function slightly simplified
  var filterTable = function(collection, filterValue) {
      if (filterValue === "") {
          return collection.toJSON();
      }
      return collection.filter(function(data) {
          return _.some(_.values(data.toJSON()), function(value) {
              return partialMatch(value, filterValue);
          });
      });
  };

Note: This method is computationally pretty inefficient, as it involves first looping over all the items in the collection, then all the fields of each item, then all words in that item value. 注意:此方法计算效率非常低,因为它涉及首先循环集合中的所有项目,然后是每个项目的所有字段,然后是该项目值中的所有单词。 In addition there are a few nested functions declared inside loops, so the memory footprint is not optimal. 此外,在循环内部声明了一些嵌套函数,因此内存占用不是最佳的。 If you have a small set of data, that should be OK, but if needed, there's a number of optimizations that can be done. 如果您有一小组数据,那应该没问题,但如果需要,可以进行一些优化。 I might come back later and edit this a bit, if I have time. 如果我有时间的话,我可能会稍后再回来编辑一下。

/code samples not tested /代码样本未经测试

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM