Как искать несколько подстрок в одном запросе

Я наткнулся на точку, от которой я не в состоянии думать дальше. Вкратце, у меня есть ul с сотнями li, каждый li имеет десятки слов в виде текста; в самом верху списка я разместил поле ввода, чтобы пользователь мог ввести некоторые ключевые слова; скажем, например, что я хочу отфильтровать из этого огромного списка только те строки, в которых есть следующие три слова в одной строке: "красная сладкая клубника". При нажатии на кнопку поиска строки отфильтровываются, и у меня есть только две строки, содержащие интересующие меня слова.

li1: "С нетерпением жду возможности съесть красную сладкую клубнику"

li2: "Сейчас лето, и красная сладкая клубника теперь свежая"

До этого момента все в порядке.

Проблема возникает, когда три искомых слова разделены другими словами или символами в строке. Итак, в верхнем примере фильтр никогда не покажет мне следующую строку:

li3: "Сейчас в продаже красная и сладкая клубника"

Итак, здесь я выкладываю всю функцию, которая фильтрует и сортирует результаты из верхнего примера:

$(document).ready(function() {
      var links = new Array();
      $("h4").each(function(index, element) {
      links.push({
            text: $(this).text(),
            element: element
        });
    });

    $("#searchbutton").click(function() {
        var query = $("#inputtext").val();
        var querywords = query.split(',');

        var results = new Array();
        for(var i = 0; i < querywords.length; i++) {
            for(var j = 0; j < links.length; j++) {
                if (links[j].text.toLowerCase().indexOf(querywords[i].toLowerCase()) > -1) {
                    results.push(links[j].element);                    
                    }
            }
        }

        $("h4").each(function(index, element) {
            this.style.display = 'none';
        });
        for(var i = 0; i < results.length; i++) {
            results[i].style.display = 'block';
        }

    });     

});

Можно ли искать несколько подстрок и получать результаты, даже если подстроки разделены символами или другими словами?


person Auto4x4Motor    schedule 18.03.2013    source источник
comment
В первом цикле for querytext не должно быть querywords?   -  person MikeM    schedule 19.03.2013
comment
Да, слова-запросы. Я обновился! На этом форуме я только что изменил весь текст запроса на слова запроса (чтобы подчеркнуть роль слов в моем примере), и я пропустил это. Спасибо. Все же это не причина.   -  person Auto4x4Motor    schedule 19.03.2013
comment
Майк М. Я попробовал ваш код, чтобы он соответствовал моей функции, но, к сожалению, из-за моего плохого понимания синтаксиса RegEx я не смог заставить его работать. Прямо сейчас я узнаю, что такое RegEx. Еще раз спасибо за ваш код.   -  person Auto4x4Motor    schedule 20.03.2013
comment
Извините, я пропустил поиск свойств текста и элементов, которые мне не нужны при использовании кода в тестировании. Я обновил код и добавил ссылку на рабочий JSFIDDLE.   -  person MikeM    schedule 20.03.2013


Ответы (2)


Вот один из способов сделать то, что, я думаю, вы хотите.

var results = new Array();
for ( var i = 0; i < querywords.length; i++ ) {
    var regex = new RegExp( 
        '(?=.*\\b' + querywords[i].split(' ').join('\\b)(?=.*\\b') + '\\b)', 'i'
    );
    for ( var j = 0; j < links.length; j++ ) {
        if ( regex.test( links[j].text ) ) {
            results.push( links[j].element );
        }
    }
}

Например, если querywords содержит элемент "red sweet strawberries", созданный regex будет

/(?=.*\bred\b)(?=.*\bsweet\b)(?=.*\bstrawberries\b)/i

Используются три положительных предварительного просмотра, чтобы строки, содержащие три слова в одной строке, проходят тест (если они окружены границей слова).

Демонстрация: JSFIDDLE.

person MikeM    schedule 19.03.2013
comment
Это потрясающая работа, MikeM! Ваше решение должно быть в верхней части поискового запроса по этой проблеме в поиске Google. Теперь я вижу, что indexOf очень ограничен для более широкой функции поиска. Большое спасибо за ваши усилия... С уважением! - person Auto4x4Motor; 20.03.2013

Если я правильно понимаю, то вся проблема в следующем:

var querywords = query.split(',');

В примере запроса у вас нет запятой. Я так понимаю это ошибка? Разделите запрос пробелом:

var querywords = query.split(' ');
person vetvicka    schedule 18.03.2013
comment
Если я разделю пробелом, то результаты вернут мне строку с красным цветом, строку со сладким и так далее. - person Auto4x4Motor; 19.03.2013
comment
В любом случае я не ищу, чтобы пользователь что-то разделил, поскольку он ожидает найти выражение из двух или трех слов вместе, а не разделенное. - person Auto4x4Motor; 19.03.2013
comment
Но почему вы разделяете запятой? Это не имеет смысла. Просто разделите запрос пробелом. Вы можете просто проверить, сколько слов запроса было найдено в каждой строке. Добавляйте только те строки, в которых были найдены все ключевые слова. Или вы можете отсортировать строки по количеству найденных слов запроса. Вы хотите, чтобы слова запроса были близко друг к другу? Убедитесь, что разница между возвращаемым значением из indexOf не слишком велика. Возможно, регулярное выражение решит вашу проблему. - person vetvicka; 19.03.2013
comment
Я думаю, что мог бы приблизиться к решению; Как я уже сказал, если я разделяю пробелом, я получаю все результаты, где найдено хотя бы одно слово; Если бы я мог фильтровать только результаты, в которых найдены все 3 слова (в одной строке), я был бы счастливым учеником; Итак, вот где я застрял: как мне отфильтровать и вернуть только строки, в которых найдены все 3 слова? - person Auto4x4Motor; 19.03.2013
comment
Vetvicka, как вы добавляете только строки, в которых были найдены все слова запроса? В случае, если функция находит все 3 слова в одном предложении. - person Auto4x4Motor; 19.03.2013