要求是:

  • 从数组中查找字符串(从此处称为调用选项),包括所有 中的术语ANY 顺序
  • 正确突出显示匹配的术语-即。在每个匹配项的前后插入一个字符串-我在这里使用<u></u>
  • 搜索查询和选项都可以是“任何”。

  • 为简单起见,答案可以集中在通过仅包含ASCII字符的列表进行不区分大小写的搜索,并假定术语定界符为纯空格-即。输入为“Foo bar baz”的查询表示搜索项为['foo', 'bar', 'baz']
    澄清:
  • 所有术语必须在匹配的选项中相互独立存在-即。较短的术语不应仅作为较长术语的子字符串存在,并且两个术语都不应与
  • 重叠
  • 该选项中重复的搜索词必须存在至少与查询
  • 相同的次数

    最终的应用程序是(可能并不奇怪)各种自动完成的。


    病毒排列
    从理论上讲,这可以通过“手动”构造一个RegExp来解决,该RegExp包含由捕获组分隔的搜索词的所有可能排列,以捕获词之间的任何内容-在(foo)(.*?)(bar)|(bar)(.*?)(foo)中搜索“foo bar”结果。
    然后使用string.replace()高亮显示一次。如果字符串有任何变化,我们将进行匹配。
    演示:

    var options = ['United States', 'United Kingdom', 'Afghanistan', 'Aland Islands', 'Albania', 'Algeria', 'American Samoa', 'Andorra', 'Angola', 'Anguilla', 'Antarctica', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Aruba', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bermuda', 'Bhutan', 'Bolivia, Plurinational State of', 'Bonaire, Sint Eustatius and Saba', 'Bosnia and Herzegovina', 'Botswana', 'Bouvet Island', 'Brazil', 'British Indian Ocean Territory', 'Brunei Darussalam', 'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Cayman Islands', 'Central African Republic', 'Chad', 'Chile', 'China', 'Christmas Island', 'Cocos (Keeling) Islands', 'Colombia', 'Comoros', 'Congo', 'Congo, The Democratic Republic of The', 'Cook Islands', 'Costa Rica', 'Cote D\'ivoire', 'Croatia', 'Cuba', 'Curacao', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Falkland Islands (Malvinas)', 'Faroe Islands', 'Fiji', 'Finland', 'France', 'French Guiana', 'French Polynesia', 'French Southern Territories', 'Gabon', 'Gambia', 'Georgia', 'Germany', 'Ghana', 'Gibraltar', 'Greece', 'Greenland', 'Grenada', 'Guadeloupe', 'Guam', 'Guatemala', 'Guernsey', 'Guinea', 'Guinea-bissau', 'Guyana', 'Haiti', 'Heard Island and Mcdonald Islands', 'Holy See (Vatican City State)', 'Honduras', 'Hong Kong', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran, Islamic Republic of', 'Iraq', 'Ireland', 'Isle of Man', 'Israel', 'Italy', 'Jamaica', 'Japan', 'Jersey', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'Korea, Democratic People\'s Republic of', 'Korea, Republic of', 'Kuwait', 'Kyrgyzstan', 'Lao People\'s Democratic Republic', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macao', 'Macedonia, The Former Yugoslav Republic of', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Martinique', 'Mauritania', 'Mauritius', 'Mayotte', 'Mexico', 'Micronesia, Federated States of', 'Moldova, Republic of', 'Monaco', 'Mongolia', 'Montenegro', 'Montserrat', 'Morocco', 'Mozambique', 'Myanmar', 'Namibia', 'Nauru', 'Nepal', 'Netherlands', 'New Caledonia', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Niue', 'Norfolk Island', 'Northern Mariana Islands', 'Norway', 'Oman', 'Pakistan', 'Palau', 'Palestinian Territory, Occupied', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Pitcairn', 'Poland', 'Portugal', 'Puerto Rico', 'Qatar', 'Reunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saint Barthelemy', 'Saint Helena, Ascension and Tristan da Cunha', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Martin (French part)', 'Saint Pierre and Miquelon', 'Saint Vincent and The Grenadines', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Sint Maarten (Dutch part)', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'South Georgia and The South Sandwich Islands', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Svalbard and Jan Mayen', 'Swaziland', 'Sweden', 'Switzerland', 'Syrian Arab Republic', 'Taiwan, Province of China', 'Tajikistan', 'Tanzania, United Republic of', 'Thailand', 'Timor-leste', 'Togo', 'Tokelau', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Turks and Caicos Islands', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'United States Minor Outlying Islands', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Venezuela, Bolivarian Republic of', 'Viet Nam', 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Wallis and Futuna', 'Western Sahara', 'Yemen', 'Zambia', 'Zimbabwe'];
    var terms, terms_esc;
    function viral_permutations() {
        var t0, t1, i, permuted, res_elm, meta_elm, regex_string, regex, li, option, match_groups, highlighted;
        meta_elm = document.getElementById('viral_permutations_meta');
        res_elm = document.getElementById('viral_permutations_result');
        res_elm.innerHTML = meta_elm.innerHTML = '';
    
        t0 = performance.now();
    
        //Split query in terms at delimiter and lowercase them
        terms = document.getElementById('viral_permutations').value.split(/\s/).filter(function(n) {
            return n;
        }).map(function(term){
            return term.toLowerCase();
        });
        meta_elm.innerHTML += 'Terms: ' + JSON.stringify(terms) + '<br>';
    
        //Escape terms
        terms_esc = terms.map(function(term) {
            return term.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
        });
    
        //Wrap terms in in individual capturing groups
        terms_esc = terms.map(function(term) {
            return '(' + term + ')';
        });
    
        //Find all permutations
        permuted = permutate_array(terms_esc);
    
        //Construct a group for each permutation
        match_groups = [];
        for (var i in permuted) {
            match_groups.push(permuted[i].join('(.*?)'));
        }
    
        try {
            //Construct the final regex
            regex_string = match_groups.join('|');
            //Display it
            document.getElementById('viral_permutations_regex').innerHTML = regex_string;
            meta_elm.innerHTML += 'RegExp length: ' + regex_string.length + '<br>';
    
            regex = new RegExp(regex_string, 'i');
    
    
            //Loop through each option
            for (i = 0; i < options.length; i++) {
                option = options[i];
    
                //Replace the terms with highlighted terms
                highlighted = option.replace(regex, viral_permutations_replace);
    
                //If anything was changed (or the query is empty) we have a match
                if (highlighted !== option || terms.length === 0) {
                    //Append it to the result list
                    li = document.createElement('li');
                    li.innerHTML = highlighted;
                    res_elm.appendChild(li);
                }
            }
    
            //Print some meta
            t1 = performance.now();
            meta_elm.innerHTML += 'Time: ' + (Math.round((t1 - t0) * 100) / 100) + 'ms';
        } catch(e) {
            meta_elm.innerHTML += '<span style="color:red">' + e.message + '</span>';
        }
    }
    
    //The replacement function
    function viral_permutations_replace() {
        var i, m, j, retval, m_cased, unmatched;
        retval = '';
    
        //Make a clone of the terms array (that we can modify without destroying the original)
        unmatched = terms.slice(0);
    
        //Loop arguments between the first (which is the full match) and
        //the last 2 (which are the offset and the full option)
        for (i = 1; i < arguments.length - 1; i++) {
            m = arguments[i];
    
            //Check that we have a string - most of the arguments will be undefined
            if (typeof m !== 'string') continue;
    
            //Lowercase the match
            m_cased = m.toLowerCase();
    
            //Append it to the return value - highlighted if it is among our terms
            j = unmatched.indexOf(m_cased);
            if (j >= 0) {
                //Remove it from the unmatched terms array
                unmatched.splice(j, 1);
                retval += '<u>' + m + '</u>';
            } else {
                retval += m;
            }
        }
        return retval;
    }
    
    //Helper function to return all possible permutations of an array
    function permutate_array(arr) {
        var perm, perm_intern;
        perm_intern = function(perm, pre, post, n) {
            var elem, i, j, ref, rest;
            if (n > 0) {
                for (i = j = 0, ref = post.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
                    rest = post.slice(0);
                    elem = rest.splice(i, 1);
                    perm_intern(perm, pre.concat(elem), rest, n - 1);
                }
            } else {
                perm.push(pre);
            }
        };
        perm = [];
        perm_intern(perm, [], arr, arr.length);
        return perm;
    }
    viral_permutations();
    <input type="text" id="viral_permutations" onkeyup="viral_permutations()">
    <p id="viral_permutations_meta"></p>
    <pre style="width:100%;overflow:auto;background:#eee" id="viral_permutations_regex"></pre>
    <ul style="height:300px;overflow:auto" id="viral_permutations_result"></ul>

    感谢Trincot指出,我的原始版本有时会两次突出显示一个重复出现的术语-在此代码段中已修复。
    失败,因为:
  • 当添加术语时,正则表达式呈指数增长。在7个字词(甚至是单个字母)时,它已超过250kb,并且我的浏览器放弃了Error: regexp too big ...

  • 其他一些不起作用的值得注意的策略:
    用每个组中的所有术语捕获组:
    (foo|bar)(.*)(foo|bar)
    
    失败,因为:
  • 将匹配包含重复项的选项-fx。 The food in the food court将匹配,尽管显然不匹配。
  • 如果我们“仔细检查”所有术语,实际上它们将找不到有效的匹配-fx。 The food in the food bar将找到foo两次,而永远不会到达bar

  • 负前瞻和反向引用:
    (foo|bar|baz)(.*?)((?!\1)(?:foo|bar|baz))(.*?)((?!\1|\3)(?:foo|bar|baz))
    
    失败,因为:
  • 只要在查询中重复使用术语,它将达到不可能的条件,例如“为我查找既不是foo也不是barbarfoobar”。
  • 我可以肯定它还有其他问题,但是当我意识到它在逻辑上有缺陷时,我停止了追求。

  • 积极向前
    (?=.*foo)(?=.*bar)(?=.*baz)
    
    失败,因为:
  • 很难(即使不是不可能)可靠地突出显示找到的匹配项。
  • 我还没有找到任何方法来确保所有术语实际上都存在-即。它们可能都单独存在于该选项中,但较短的术语只能作为较长术语的子串存在-否则术语可能会重叠。
  • 最佳答案

    我建议对分而治之的想法稍作改动:您可以“清除”已匹配的字符,然后对该字符串进行进一步搜索,而不是将字符串拆分为大块(叮咬)。要消除的字符将是分隔符,因为保证在任何术语中都不会出现。

    这里是:

    function trincotWipeSearch(query, options, separator) {
        // Split query in terms at delimiter
        const terms = query.split(separator).filter(Boolean);
        if (!terms.length) return options;
        // Sort terms by descending size
        terms.sort( (a,b) => b.length - a.length );
    
        // Escape terms, and enrich with size of original term
        // and a string of the same length filled with the separator char
        const items = terms.map(term => ({
            size: term.length,
            wipe: separator.repeat(term.length),
            regex: new RegExp(term.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'gi')
        }));
    
        function getOffsets(termIndex, text) {
            // All terms found?
            if (termIndex >= terms.length) return [];
            let match;
            const { regex, size, wipe } = items[termIndex];
            regex.lastIndex = 0;
            while (match = regex.exec(text)) {
                let index = match.index;
                // Wipe characters and recurse to find other terms
                let offsets = getOffsets(termIndex+1,
                    text.substr(0, index) + wipe + text.substr(index + size));
                if (offsets !== undefined) {
                    // Solution found, backtrack all the way
                    return offsets.concat([index, index + size]);
                }
                regex.lastIndex = match.index + 1;
            }
        }
    
        // Loop through each option
        return options.map( option => {
            // Get the offsets of the matches
            let offsets = getOffsets(0, option);
            if (offsets) {
                // Apply the offsets to add the markup
                offsets
                    .sort( (a,b) => b - a )
                    .map((index, i) => {
                        option = option.substr(0, index)
                            + (i%2 ? "<u>" : "</u>")
                            + option.substr(index);
                    });
                return option;
            }
        }).filter(Boolean); // get only the non-empty results
    }
    
    var options = ['United States', 'United Kingdom', 'Afghanistan', 'Aland Islands', 'Albania', 'Algeria', 'American Samoa', 'Andorra', 'Angola', 'Anguilla', 'Antarctica', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Aruba', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bermuda', 'Bhutan', 'Bolivia, Plurinational State of', 'Bonaire, Sint Eustatius and Saba', 'Bosnia and Herzegovina', 'Botswana', 'Bouvet Island', 'Brazil', 'British Indian Ocean Territory', 'Brunei Darussalam', 'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Cayman Islands', 'Central African Republic', 'Chad', 'Chile', 'China', 'Christmas Island', 'Cocos (Keeling) Islands', 'Colombia', 'Comoros', 'Congo', 'Congo, The Democratic Republic of The', 'Cook Islands', 'Costa Rica', 'Cote D\'ivoire', 'Croatia', 'Cuba', 'Curacao', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Falkland Islands (Malvinas)', 'Faroe Islands', 'Fiji', 'Finland', 'France', 'French Guiana', 'French Polynesia', 'French Southern Territories', 'Gabon', 'Gambia', 'Georgia', 'Germany', 'Ghana', 'Gibraltar', 'Greece', 'Greenland', 'Grenada', 'Guadeloupe', 'Guam', 'Guatemala', 'Guernsey', 'Guinea', 'Guinea-bissau', 'Guyana', 'Haiti', 'Heard Island and Mcdonald Islands', 'Holy See (Vatican City State)', 'Honduras', 'Hong Kong', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran, Islamic Republic of', 'Iraq', 'Ireland', 'Isle of Man', 'Israel', 'Italy', 'Jamaica', 'Japan', 'Jersey', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'Korea, Democratic People\'s Republic of', 'Korea, Republic of', 'Kuwait', 'Kyrgyzstan', 'Lao People\'s Democratic Republic', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macao', 'Macedonia, The Former Yugoslav Republic of', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Martinique', 'Mauritania', 'Mauritius', 'Mayotte', 'Mexico', 'Micronesia, Federated States of', 'Moldova, Republic of', 'Monaco', 'Mongolia', 'Montenegro', 'Montserrat', 'Morocco', 'Mozambique', 'Myanmar', 'Namibia', 'Nauru', 'Nepal', 'Netherlands', 'New Caledonia', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Niue', 'Norfolk Island', 'Northern Mariana Islands', 'Norway', 'Oman', 'Pakistan', 'Palau', 'Palestinian Territory, Occupied', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Pitcairn', 'Poland', 'Portugal', 'Puerto Rico', 'Qatar', 'Reunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saint Barthelemy', 'Saint Helena, Ascension and Tristan da Cunha', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Martin (French part)', 'Saint Pierre and Miquelon', 'Saint Vincent and The Grenadines', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Sint Maarten (Dutch part)', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'South Georgia and The South Sandwich Islands', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Svalbard and Jan Mayen', 'Swaziland', 'Sweden', 'Switzerland', 'Syrian Arab Republic', 'Taiwan, Province of China', 'Tajikistan', 'Tanzania, United Republic of', 'Thailand', 'Timor-leste', 'Togo', 'Tokelau', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Turks and Caicos Islands', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'United States Minor Outlying Islands', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Venezuela, Bolivarian Republic of', 'Viet Nam', 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Wallis and Futuna', 'Western Sahara', 'Yemen', 'Zambia', 'Zimbabwe'];
    
    /*
     * I/O and performance measurements
     */
    
    function processInput() {
        var query = this.value.toLowerCase();
        const t0 = performance.now();
        const matches = trincotWipeSearch(query, options, ' ');
        const spentTime = performance.now() - t0;
        // Output the time spent
        time.textContent = spentTime.toFixed(2);
        // Output the matches
        result.innerHTML = '';
        for (var match of matches) {
            // Append it to the result list
            var li = document.createElement('li');
            li.innerHTML = match;
            result.appendChild(li);
        }
    }
    
    findTerms.addEventListener('keyup', processInput);
    processInput.call(findTerms);
    ul {
        height:300px;
        font-size: smaller;
        overflow: auto;
    }
    Input terms: <input type="text" id="findTerms"><br>
    
    <h3>Trincot's Wipe Search</h3>
    Time: <span id="time"></span>ms<br>
    <ul id="result"></ul>


    我从时间测量中排除了DOM I/O。

    这是一个jsfiddle并排比较两种算法。仍然不难添加第三种算法来与其他算法进行比较。

    当分隔符可以是任何正则表达式时

    ...那么上述功能将无法使用。解决此问题的一种方法是引入“影子”字符串,该字符串与选项字符串一样长,但是其中只有两个不同的可能字符(例如.x):
  • 二者之一将表示选项字符串中的对应字符(即,在相同位置)已与一个词匹配,因此不再可用于另一个词的匹配。
  • 其他字符将指示选项字符串中的相应字符仍可用于包含在术语的匹配项中。

  • 显然,这会使函数稍微慢一点,因为在检查此阴影字符串后可能需要拒绝匹配项:

    function trincotShadowMarks (query, options, separator) {
        // Split query in terms at delimiter
        const terms = query.split(separator).filter(Boolean);
        if (!terms.length) return options;
        // Sort terms by descending size
        terms.sort( (a,b) => b.length - a.length );
    
        // Escape terms, and enrich with size of original term
        // and a string of the same length filled with the separator char
        const items = terms.map(term => ({
            size: term.length,
            used: 'x'.repeat(term.length),
            free: '.'.repeat(term.length),
            regex: new RegExp(term.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'gi')
        }));
    
        function getOffsets(termIndex, text, shadow) {
            // All terms found?
            if (termIndex >= terms.length) return [];
            let match;
            const { regex, size, used, free } = items[termIndex];
            regex.lastIndex = 0;
            while (regex.lastIndex > -1 && (match = regex.exec(text))) {
                let index = match.index;
                // Is this match not overlapping with another match?
                if (!shadow.substr(index, size).includes('x')) {
                    // Mark position as used and recurse to find other terms
                    let offsets = getOffsets(termIndex+1, text,
                        shadow.substr(0, index) + used + shadow.substr(index + size));
                    if (offsets !== undefined) {
                        // Solution found, backtrack all the way
                        return offsets.concat([index, index + size]);
                    }
                }
                regex.lastIndex = shadow.indexOf(free, match.index + 1);
            }
        }
    
        // Loop through each option
        return options.map( option => {
            // Get the offsets of the matches
            let offsets = getOffsets(0, option, '.'.repeat(option.length));
            if (offsets) {
                // Apply the offsets to add the markup
                offsets
                    .sort( (a,b) => b - a )
                    .map((index, i) => {
                        option = option.substr(0, index)
                            + (i%2 ? "<u>" : "</u>")
                            + option.substr(index);
                    });
                return option;
            }
        }).filter(Boolean); // get only the non-empty results
    }
    
    var options = ['United States', 'United Kingdom', 'Afghanistan', 'Aland Islands', 'Albania', 'Algeria', 'American Samoa', 'Andorra', 'Angola', 'Anguilla', 'Antarctica', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Aruba', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bermuda', 'Bhutan', 'Bolivia, Plurinational State of', 'Bonaire, Sint Eustatius and Saba', 'Bosnia and Herzegovina', 'Botswana', 'Bouvet Island', 'Brazil', 'British Indian Ocean Territory', 'Brunei Darussalam', 'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Cayman Islands', 'Central African Republic', 'Chad', 'Chile', 'China', 'Christmas Island', 'Cocos (Keeling) Islands', 'Colombia', 'Comoros', 'Congo', 'Congo, The Democratic Republic of The', 'Cook Islands', 'Costa Rica', 'Cote D\'ivoire', 'Croatia', 'Cuba', 'Curacao', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Falkland Islands (Malvinas)', 'Faroe Islands', 'Fiji', 'Finland', 'France', 'French Guiana', 'French Polynesia', 'French Southern Territories', 'Gabon', 'Gambia', 'Georgia', 'Germany', 'Ghana', 'Gibraltar', 'Greece', 'Greenland', 'Grenada', 'Guadeloupe', 'Guam', 'Guatemala', 'Guernsey', 'Guinea', 'Guinea-bissau', 'Guyana', 'Haiti', 'Heard Island and Mcdonald Islands', 'Holy See (Vatican City State)', 'Honduras', 'Hong Kong', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran, Islamic Republic of', 'Iraq', 'Ireland', 'Isle of Man', 'Israel', 'Italy', 'Jamaica', 'Japan', 'Jersey', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'Korea, Democratic People\'s Republic of', 'Korea, Republic of', 'Kuwait', 'Kyrgyzstan', 'Lao People\'s Democratic Republic', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macao', 'Macedonia, The Former Yugoslav Republic of', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Martinique', 'Mauritania', 'Mauritius', 'Mayotte', 'Mexico', 'Micronesia, Federated States of', 'Moldova, Republic of', 'Monaco', 'Mongolia', 'Montenegro', 'Montserrat', 'Morocco', 'Mozambique', 'Myanmar', 'Namibia', 'Nauru', 'Nepal', 'Netherlands', 'New Caledonia', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Niue', 'Norfolk Island', 'Northern Mariana Islands', 'Norway', 'Oman', 'Pakistan', 'Palau', 'Palestinian Territory, Occupied', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Pitcairn', 'Poland', 'Portugal', 'Puerto Rico', 'Qatar', 'Reunion', 'Romania', 'Russian Federation', 'Rwanda', 'Saint Barthelemy', 'Saint Helena, Ascension and Tristan da Cunha', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Martin (French part)', 'Saint Pierre and Miquelon', 'Saint Vincent and The Grenadines', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Sint Maarten (Dutch part)', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'South Georgia and The South Sandwich Islands', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Svalbard and Jan Mayen', 'Swaziland', 'Sweden', 'Switzerland', 'Syrian Arab Republic', 'Taiwan, Province of China', 'Tajikistan', 'Tanzania, United Republic of', 'Thailand', 'Timor-leste', 'Togo', 'Tokelau', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Turks and Caicos Islands', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'United States Minor Outlying Islands', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Venezuela, Bolivarian Republic of', 'Viet Nam', 'Virgin Islands, British', 'Virgin Islands, U.S.', 'Wallis and Futuna', 'Western Sahara', 'Yemen', 'Zambia', 'Zimbabwe'];
    
    /*
     * I/O and performance measurements
     */
    
    function processInput() {
        var query = this.value.toLowerCase();
        const t0 = performance.now();
        const matches = trincotShadowMarks(query, options, ' ');
        const spentTime = performance.now() - t0;
        // Output the time spent
        time.textContent = spentTime.toFixed(2);
        // Output the matches
        result.innerHTML = '';
        for (var match of matches) {
            // Append it to the result list
            var li = document.createElement('li');
            li.innerHTML = match;
            result.appendChild(li);
        }
    }
    
    findTerms.addEventListener('keyup', processInput);
    processInput.call(findTerms);
    ul {
        height:300px;
        font-size: smaller;
        overflow: auto;
    }
    Input terms: <input type="text" id="findTerms"><br>
    
    <h3>Trincot's Wipe Search</h3>
    Time: <span id="time"></span>ms<br>
    <ul id="result"></ul>

    09-10 01:59
    查看更多