【问题标题】:Filter by includes and then sort by startsWith按包含过滤,然后按startsWith排序
【发布时间】:2021-09-22 17:40:36
【问题描述】:

我正在实现一个typeahead,我们需要过滤它应该开始(排序)结果的记录StartsWith,然后includes

样本数据:

Dotnet Developer  
Azure Administrator  
Microsoft Azure  
Azure DevOps  
UX Developer  
IOT Azure  

如果我开始输入Azu,那么它应该按以下顺序显示结果。

  • Azu是管理员
  • Azu重新开发运维
  • 微软Azu重新
  • 物联网Azu重新

也就是说,我们正在寻找从逻辑开始然后包含逻辑的结果。

我尝试了以下方法,但没有奏效,有人可以指导我吗?

abc.filter(v => v.includes(filterTerm)).sort(function(a, b) {
  return a.startsWith(filterTerm)
});  

还附上jsfiddle 和一些示例数据。

【问题讨论】:

  • 你应该返回一个数字给排序函数,而不是一个布尔值

标签: javascript jquery arrays json typescript


【解决方案1】:

我会提前对列表进行排序。现在,您只需要对 input 事件进行去抖动处理,并使用正则表达式来突出显示结果中的匹配项。

const data = `
  Dotnet Developer  
  Azure Administrator  
  Microsoft Azure  
  Azure DevOps  
  UX Developer  
  IOT Azure 
`.trim().split('\n').map(line => line.trim()).sort();

// https://www.joshwcomeau.com/snippets/javascript/debounce/
const debounce = (callback, wait) => {
  let timeoutId = null;
  return (...args) => {
    window.clearTimeout(timeoutId);
    timeoutId = window.setTimeout(() => {
      callback.apply(null, args);
    }, wait);
  };
}

const emptyEl = (el) => {
  while (el.firstChild) el.firstChild.remove();
};

const renderResults = (target, term, hits) => {
  emptyEl(target);
  if (term.length > 0 && hits.length > 0) {
    const regex = new RegExp(`(${term})`, 'i');
    const listEl = document.createElement('ul');
    hits.forEach(hit => {
      const listItemEl = document.createElement('li');
      listItemEl.innerHTML = hit.replace(regex, '<strong>$1</strong>');
      listEl.append(listItemEl);
    });
    target.append(listEl);
  } else {
    const emptyEl = document.createElement('p');
    emptyEl.textContent = 'No Results...';
    target.append(emptyEl);
  }
}

const onSearch = debounce((e) => {
  const term = e.target.value.trim();
  const results = document.querySelector('.results');
  const hits = data.filter(text => text.includes(term));
  renderResults(results, term, hits);
}, 250);

document.querySelector('.search').addEventListener('input', onSearch);
strong { font-weight: bold; color: green; }
<input type="search" class="search" placeholder="Search..." />
<h2>Results</h2>
<div class="results">
  <p>No Results...</p>
</div>

【讨论】:

    【解决方案2】:

    您可以计算一个分数(0 或 1)来表示结果的优先级,并使用它来排序:

    const abc = [
      "Dotnet Developer (should not appear)",
      "Azure Administrator (should be first)",
      "Microsoft Azure (should be second)",
      "Azure DevOps (should be first)",
      "UX Developer (should not appear)",
      "IOT Azure (should be second)",
    ];
    
    const filterTerm = "Azu";
    
    const score = searched => string => string.startsWith(searched) ? 1 : 0;
    
    const result = abc.filter(v => v.includes(filterTerm)).sort((a, b) => {
      const [aScore, bScore] = [a, b].map(score(filterTerm));
      return bScore - aScore;
    });
    
    console.log(result);

    【讨论】:

      【解决方案3】:

      像这样更改您的自定义排序。当b 以您的首选字符串开头时,我将返回 1。返回 1 时,将 b 放在 a 前面。

      var abc = ['test', 'test 123', 'test 234', 'abc', 'abc 123', 'xyz', '123', '1235'];
      var filterTerm = '123';
      
      var includesList = abc.filter(v => v.includes(filterTerm));
      
      var sortedList = abc.filter(v => v.includes(filterTerm)).sort(function(a, b) {
        if(b.startsWith(filterTerm)) return 1;
        return -1;
      });
      
      console.log(sortedList);

      【讨论】:

      • 排序函数不应该只使用两个参数之一
      • 哦。有什么具体原因吗?
      • 因为每个“组”中项目的顺序可能取决于浏览器对Array.prototype.sort的实现
      【解决方案4】:

      我认为这可行:

      const search = (filterTerm, terms) => {
          filterTerm = filterTerm.toLowerCase();
          return terms
              .filter(v => v.toLowerCase().includes(filterTerm))
              .sort((a, b) => {
                  const aStarts = a.toLowerCase().startsWith(filterTerm);
                  const bStarts = b.toLowerCase().startsWith(filterTerm);
                  if (aStarts && bStarts) return a.localeCompare(b);
                  if (aStarts && !bStarts) return -1;
                  if (!aStarts && bStarts) return 1;
                  return a.localeCompare(b);
              });
      };
      
      console.log(search("azu", [
          "IOT Azure",
          "Dotnet Developer",
          "Microsoft Azure",
          "Azure DevOps",
          "UX Developer",
          "Azure Administrator",
      ]));

      如果您的术语列表是预定义和固定的,但搜索频繁发生,您可以简化功能以供重复使用:

      const search = terms => filterTerm => {
          filterTerm = filterTerm.toLowerCase();
          return terms
              .filter(v => v.toLowerCase().includes(filterTerm))
              .sort((a, b) => {
                  const aStarts = a.toLowerCase().startsWith(filterTerm);
                  const bStarts = b.toLowerCase().startsWith(filterTerm);
                  if (aStarts && bStarts) return a.localeCompare(b);
                  if (aStarts && !bStarts) return -1;
                  if (!aStarts && bStarts) return 1;
                  return a.localeCompare(b);
              });
      };
      
      const programmingTerms = search([
          "IOT Azure",
          "Dotnet Developer",
          "Microsoft Azure",
          "Azure DevOps",
          "UX Developer",
          "Azure Administrator",
      ]);
      

      用法:

      programmingTerms("Azu");  // -> ["Azure Administrator", "Azure DevOps", "IOT Azure", "Microsoft Azure"]
      programmingTerms("Dev");  // -> ["Azure DevOps", "Dotnet Developer", "UX Developer"]
      

      【讨论】:

        【解决方案5】:

        我已经调整了排序功能,它现在可以工作了。你需要给排序函数一个数字。

        var abc = ['test', 'test 123', 'test 234', 'abc', 'abc 123', 'xyz', '123', '1235'];
        var filterTerm = '123';
        
        var includesList = abc.filter(v => v.includes(filterTerm));
        
        var sortedList = includesList.sort((a, b) => {
          return a.indexOf(filterTerm) < b.indexOf(filterTerm) ? -1 : 1;
        });
        
        console.log(sortedList)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-07-04
          • 1970-01-01
          • 2016-01-15
          相关资源
          最近更新 更多