如何淘汰搜索多个关键字以及实时搜索

时间:2016-07-15 14:49:15

标签: javascript knockout.js

我拼命想让淘汰赛对我的项目进行实时搜索。此搜索需要能够区分查询中的空格并将其用作分离的搜索查询。例如;如果用户输入" Matrix"它可以同时播放DVD和Bluray Matrix电影...但如果用户输入Matrix DVD则没有任何内容。

我试图让它搜索查询中空格分隔的数据中的所有内容。

这是我的小提琴:https://jsfiddle.net/neosketo/eLgscy1t/10/

HTML:

<div id="container">
<h1>Movie Search</h1>
<form>
  <input id="search" type="search" name="search" placeholder="Search for a user" data-bind="value: query, valueUpdate: 'keyup'" autocomplete="off" />
</form>

<table data-bind="visible: movies().length > 0">
  <thead><tr><th>Type</th><th>Name</th><th>Media</th></tr></thead>
  <tbody data-bind="foreach: movies">
    <tr>
      <td data-bind="text: type"></td>
      <td data-bind="text: name"></td>
      <td data-bind="text: media"></td>
    </tr>    
  </tbody>
</table>
</div>

JAVASCRIPT:

var movies = [
  { type: 'action',     name: 'The Matrix',                 media: 'DVD'},
  { type: 'action',     name: 'The Matrix',                 media: 'Bluray'},
  { type: 'fantasy',    name: 'Lord of the Rings',  media: 'DVD'},
  { type: 'fantasy',    name: 'Lord of the Rings',  media: 'Bluray'},
  { type: 'comedy',     name: 'Dumb and Dumber',        media: 'DVD'},
  { type: 'comedy',     name: 'Dumb and Dumber',        media: 'Bluray'}
];

var viewModel = {
  movies: ko.observableArray([]),
  query: ko.observable(''),

  search: function(value) {
    viewModel.movies.removeAll();

    if (value == '') return;

    for (var movie in movies) {
      if (movies[movie].name.toLowerCase().indexOf(value.toLowerCase()) >= 0) {
        viewModel.movies.push(movies[movie]);
      }
    }
  }
};

viewModel.query.subscribe(viewModel.search);
ko.applyBindings(viewModel);

2 个答案:

答案 0 :(得分:2)

我首先要扩展您的电影对象,以包含他们自己的搜索字词集合。每次键入字符时都不想重新计算:

var moviesWithSearchString = movies.map(function(movie) {
  var ogKeys = Object.keys(movie);
  return Object.assign({}, movie, {
    _searchProps: ogKeys
      .map(function(key) {
        return movie[key].toLowerCase();
      })
  });
});

_searchProps属性包含一系列搜索词。第一部电影:["action", "the matrix", "dvd"]

现在,在您的search方法中,我们还需要将搜索查询转换为小写单词数组:

var searchVals = value
  .toLowerCase()
  .split(" ");

然后,有趣的部分开始了。我们希望每个Array.prototype.every)搜索字出现String.prototype.includes至少一次({{3我们电影的属性:

viewModel.movies(moviesWithSearchString.filter(function(movie) {
  return searchVals.every(function(searchVal) {
    return movie._searchProps.some(function(searchProp) {
      return searchProp.includes(searchVal);
    });
  });
}));

工作示例:

小提琴:Array.prototype.some

stack snippet:

var movies = [{
  type: 'action',
  name: 'The Matrix',
  media: 'DVD'
}, {
  type: 'action',
  name: 'The Matrix',
  media: 'Bluray'
}, {
  type: 'fantasy',
  name: 'Lord of the Rings',
  media: 'DVD'
}, {
  type: 'fantasy',
  name: 'Lord of the Rings',
  media: 'Bluray'
}, {
  type: 'comedy',
  name: 'Dumb and Dumber',
  media: 'DVD'
}, {
  type: 'comedy',
  name: 'Dumb and Dumber',
  media: 'Bluray'
}];

var moviesWithSearchString = movies.map(function(movie) {
  var ogKeys = Object.keys(movie);
  return Object.assign({}, movie, {
    _searchProps: ogKeys
      .map(function(key) {
        return movie[key].toLowerCase();
      })
  });
});

var viewModel = {
  movies: ko.observableArray([]),
  query: ko.observable(''),

  search: function(value) {
    var searchVals = value
      .toLowerCase()
      .split(" ");

    viewModel.movies(moviesWithSearchString.filter(function(movie) {
      return searchVals.every(function(searchVal) {
        return movie._searchProps.some(function(searchProp) {
          return searchProp.includes(searchVal);
        });
      });
    }));
  }
};

viewModel.query.subscribe(viewModel.search);
ko.applyBindings(viewModel);
body {
  margin: 2em;
  text-align: center;
}
form {
  margin-bottom: 2em;
}
table {
  text-align: left;
  margin: 0 auto;
}
th,
td {
  padding: .2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div id="container">
  <h1>Movie Search</h1>
  <form>
    <input id="search" type="search" name="search" placeholder="Search for a user" data-bind="value: query, valueUpdate: 'keyup'" autocomplete="off" />
  </form>

  <table data-bind="visible: movies().length > 0">
    <thead>
      <tr>
        <th>Type</th>
        <th>Name</th>
        <th>Media</th>
      </tr>
    </thead>
    <tbody data-bind="foreach: movies">
      <tr>
        <td data-bind="text: type"></td>
        <td data-bind="text: name"></td>
        <td data-bind="text: media"></td>
      </tr>
    </tbody>
  </table>
</div>

请注意,旧浏览器不支持某些ArrayString方法。查看关于polyfills的链接文章。

答案 1 :(得分:0)

您可以按空格拆分查询字词,然后使用.some数组方法搜索电影。

search: function(value) {
  viewModel.movies.removeAll();

  if (!value) return;

  var values = value.split(' ');

  for (var movie in movies) {
    var found = values.some(function(word) {
        return !!word && movies[movie].name.toLowerCase().indexOf(word.toLowerCase()) >= 0;
    });
    if (found) {
      viewModel.movies.push(movies[movie]);
    }
  }
}

!!word只是在用户输入多个空格的情况下短暂搜索,导致分割中出现空字符串。

小提琴:https://jsfiddle.net/eLgscy1t/12/