我试图找到一种快速的方法来搜索数组元素中的部分字符串,如果输入值大于2个字符,则将它们放入新数组中.

最终,这个数组将用于在找到结果时创建一个列表,即本地数据搜索.

这可以很好地工作,但数组可能非常大(数千个条目),并且正在寻找不会导致性能问题的最佳方法. 他说:

<html>
    <body>
        <input id="test">
    </body>
    <script>

        let test = ['Fruits: Apples', 'Fruits: Oranges', 'Fruits: Banannas', 'Fruits: Lemons', 'Vegetables: Corn', 'Vegetables: Potatoes']
        let found = []

        document.querySelector('#test').addEventListener('keyup', e => {
            let value = e.currentTarget.value
            if (value.length > 2) {
                let index = test.findIndex(i => i.includes(value))
                test.forEach(v => {
                    if(v.includes(value) && !found.includes(v))
                        found.push(v)
                })
            } else {
                found = []
            }
            console.log(found)
        })
    </script>
</html>

推荐答案

  1. 将数组转换为SET和BACK.因此,您将拥有一个唯一的数组,并且不需要在以后判断值.
  2. 使用Array::filter()只需过滤array.
  3. 您还可以使用预编译的正则表达式来使搜索不区分大小写.

let test = ['Fruits: Apples', 'Fruits: Oranges', 'Fruits: Banannas', 'Fruits: Lemons', 'Vegetables: Corn', 'Vegetables: Potatoes']
let unique = [...new Set(test)];
let found = []

document.querySelector('#test').addEventListener('keyup', e => {
    let value = e.currentTarget.value
    if (value.length > 2) {
        const regex = new RegExp(value, 'i');
        found = unique.filter(v => regex.test(v));
    } else {
        found = []
    }
    console.log(found)
})
<input id="test">

一个更复杂和更高性能的 Select 可能是构建一个后缀数组并对其进行二进制搜索:

const source = ['Fruits: Apples', 'Fruits: Oranges', 'Fruits: Bananas', 'Fruits: Lemons', 'Vegetables: Corn', 'Vegetables: Potatoes'];
const test = [...new Set(source)].map(v => v.toLowerCase());

// build a suffix array
let suff = test.reduce((r, v, i) => ([...[...v].keys()].forEach(idx => r.push([i, idx])), r), []).sort((a, b) => {
  a = test[a[0]].slice(a[1]);
  b = test[b[0]].slice(b[1]);
  return a > b ? 1 : -1;
}).flat();

let found = [];

document.querySelector('#test').addEventListener('keyup', e => {
    let value = e.currentTarget.value.toLowerCase();
    found = [];
    if (value.length > 2) {
      // binary search the suffix array
      let left = -1;
      let low = 0, high = suff.length/2, mid;
      while (low <= high){
        mid = (low + high) / 2 | 0;
        const i=mid*2;
        const v = test[suff[i]].slice(suff[i+1]);
        const prev = test[suff[i-2]]?.slice(suff[i-1]);
        if (v.startsWith(value) && !prev.startsWith(value)) {
           left = mid;
           break;
        } if (value > v)
          low = mid + 1;
        else            
          high = mid - 1;
      }
      if(left >= 0){
        // binary search the suffix array, right bound
        let right = left;
        let low = left, high = suff.length/2, mid;
        while (low <= high){
          mid = (low + high) / 2 | 0;
          const i = mid*2;
          const v = test[suff[i]].slice(suff[i+1]);
          const next = test[suff[i+2]]?.slice(suff[i+3]);
          const starts = v.startsWith(value);
          if (starts && !next.startsWith(value)) {
             right = mid;
             break;
          } if (value > v || starts)
            low = mid + 1;
          else            
            high = mid - 1;
        }
        const idxs = new Uint32Array(right - left + 1);
        for(let i=left;i<=right;i++){
          idxs[i - left] = suff[i*2];
        }
        idxs.sort();
        let prev;
        for(let i=0;i<idxs.length;i++){
          if(prev === idxs[i]) continue;
          found.push(source[prev = idxs[i]]);
        }
      }
    }
    console.log(found)
})
<input id="test">

如您所见,后缀数组可以快19倍.

` Chrome/121
----------------------------------------------------
suffix array     1.00x | x100000 569 582 583 589 593
filter & regex  19.16x |   x1000 109 112 113 113 113
----------------------------------------------------
https://github.com/silentmantra/benchmark `

const chunk = ["apple","apricot","avocado","banana","bell pepper","bilberry","blackberry","blackcurrant","blood orange","blueberry","boysenberry","breadfruit","canary melon","cantaloupe","cherimoya","cherry","chili pepper","clementine","cloudberry","coconut","cranberry","cucumber","currant","damson","date","dragonfruit","durian","eggplant","elderberry","feijoa","fig","goji berry","gooseberry","grape","grapefruit","guava","honeydew","huckleberry","jackfruit","jambul","jujube","kiwi fruit","kumquat","lemon","lime","loquat","lychee","mandarine","mango","mulberry","nectarine","nut","olive","orange","papaya","passionfruit","peach","pear","persimmon","physalis","pineapple","plum","pomegranate","pomelo","purple mangosteen","quince","raisin","rambutan","raspberry","redcurrant","rock melon","salal berry","satsuma","star fruit","strawberry","tamarillo","tangerine","tomato","ugli fruit","watermelon"];

const source = [];
let count = 100;
while(count--) source.push(...chunk.map(v => v + ' ' + count));

const value = 'ana';
let unique = [...new Set(source)];

const test = [...new Set(source)].map(v => v.toLowerCase());

// build a suffix array
let suff = Uint32Array.from(test.reduce((r, v, i) => ([...[...v].keys()].forEach(idx => r.push([i, idx])), r), []).sort((a, b) => {
  a = test[a[0]].slice(a[1]);
  b = test[b[0]].slice(b[1]);
  return a > b ? 1 : -1;
}).flat());


// @benchmark filter & regex
const regex = new RegExp(value, 'i');
const result = unique.filter(v => regex.test(v));
[result.length, result];

// @benchmark suffix array

let found = [];

// binary search the suffix array, left bound
let left = -1;
let low = 0, high = suff.length/2, mid;
while (low <= high){
  mid = (low + high) / 2 | 0;
  const i=mid*2;
  const v = test[suff[i]].slice(suff[i+1]);
  const prev = test[suff[i-2]]?.slice(suff[i-1]);
  if (v.startsWith(value) && !prev.startsWith(value)) {
     left = mid;
     break;
  } if (value > v)
    low = mid + 1;
  else            
    high = mid - 1;
}
if(left >= 0){
  // binary search the suffix array, right bound
  let right = left;
  let low = left, high = suff.length/2, mid;
  while (low <= high){
    mid = (low + high) / 2 | 0;
    const i = mid*2;
    const v = test[suff[i]].slice(suff[i+1]);
    const next = test[suff[i+2]]?.slice(suff[i+3]);
    const starts = v.startsWith(value);
    if (starts && !next.startsWith(value)) {
       right = mid;
       break;
    } if (value > v || starts)
      low = mid + 1;
    else            
      high = mid - 1;
  }
  const idxs = new Uint32Array(right - left + 1);
  for(let i=left;i<=right;i++){
    idxs[i - left] = suff[i*2];
  }
  idxs.sort();
  let prev;
  for(let i=0;i<idxs.length;i++){
    if(prev === idxs[i]) continue;
    found.push(source[prev = idxs[i]]);
  }
}
[found.length, found];

/*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));

Javascript相关问答推荐

在页面上滚动 timeshift 动垂直滚动条

为什么当我解析一个promise时,输出处于挂起状态?

如何用拉威尔惯性Vue依赖下拉?

在带有背景图像和圆形的div中添加长方体阴影时的重影线

Angular 中的类型错误上不存在获取属性

Chart.js-显示值应该在其中的引用区域

为什么123[';toString';].long返回1?

用JS从平面文件构建树形 struct 的JSON

用于在路径之间移动图像的查询

Use Location位置成员在HashRouter内为空

为什么可选参数的顺序会导致问题?

MongoDB受困于太多的数据

OnClick更改Json数组JSX中的图像源

当我点击一个按钮后按回车键时,如何阻止它再次被点击

使用RxJS from Event和@ViewChild vs KeyUp事件和RxJS主题更改输入字段值

Clip-Path在网页浏览器(Mozilla、Edge、Chrome)上不能正常工作,但在预览版Visual Code Studio HTML、CSS、JS上却能很好地工作

MongoDB通过数字或字符串过滤列表

P5play SecurityError:无法从';窗口';读取命名属性';Add';:阻止具有源的帧访问跨源帧

Promise.race()返回已解析的promise ,而不是第一个被拒绝的promise

我如何才能让p5.js在不使用实例模式的情况下工作?