Несколько ключевых слов поиска по списку списков

1

Прошло уже пару дней, пытаясь выяснить, какой самый эффективный способ поиска списка списков с тысячами элементов с несколькими ключевыми словами без определенного порядка. Я реализовал простой поиск на других экранах, но это немного сложно. Позволь мне объяснить.

поэтому давайте скажем, что мои списки выглядят следующим образом:

  1. Боевое искусство
    • Игра смерти
    • IP Man
    • Защитник
  2. комедия
    • Кататься одному
    • Академия полиции
    • телеведущий
    • Управление гневом
  3. действие
    • Кровь и кости
    • Матрица
    • Капитан Америка
    • Терминатор

и если я наберу в поле поиска ключевое слово "человек", я ожидаю увидеть 3 удара, что легко выполнить. Однако, если я набираю частичные ключевые слова, такие как "ma" или "ma a", я ожидаю увидеть только управление гневом. Если есть более одного удара, я должен уметь сузить его, набрав больше слов или просто заполнив свои ключевые слова, которые у меня есть в окне поиска.

Сейчас у меня есть реализация, но я не слишком доволен ею из-за количества итераций, которые я делаю. Я боюсь, что, когда у нас будет больше данных, этот поиск не будет работать эффективно и будет медленным.

public List<ValidAndCompletedModel> Filter(List<string> searchTerms)
{
    var validAndCompleted = new List<ValidAndCompletedModel>();

    foreach (var searchTerm in searchTerms)
    {
        var containingList = ValidAndCompleted.Where(d => ListsContainsSimilarEntry(searchTerm, d.EnglishTranslationTerms)).ToList();
        containingList = containingList.Distinct().ToList();
        validAndCompleted.AddRange(containingList.Where(d => ListsStartsWithSimilarEntry(searchTerm, d.EnglishTranslationTerms)).ToList());
    }
    validAndCompleted = validAndCompleted.Distinct().ToList();


    return validAndCompleted;
}

private bool ListsStartsWithSimilarEntry(string searchTerm, IEnumerable<string> searchList)
        {
            var available = searchList.Any(sl => sl.StartsWith(searchTerm));
            return available;
        }
private bool ListsContainsSimilarEntry(string searchTerm, IEnumerable<string> searchList)
            {
                var available = searchList.Any(sl => sl.Contains(searchTerm));
                return available;
            }

Ваша помощь очень ценится.

Теги:
wpf
search
mvvm

2 ответа

0

Используя пример ваших фильмов и категорий. Допустим, у нас есть класс:

public class Category
{
    public string Name {get; set;}
    public List<string> Films {get; set;}
}

И наша установка фильмов такова:

var filmCategories = new List<Category>
{
new Category { Name = "Martial Arts", Films = new List<string>
        { "Game Of Death", "IP Man", "The Protector" }},            
    new Category { Name = "Comedy", Films = new List<string>
        { "Ride Along", "Police Academy", "Anchorman", "Anger Management" }},           
    new Category { Name = "Action", Films = new List<string>
        { "Blood And Bone", "The Matrix", "Captain America", "The Terminator" }}
};  

Наши условия поиска таковы:

var searchTerm = "g d";
var searchTerms = searchTerm.ToLower().Split(' ');

Вы можете получить результаты поиска в 1 выстреле следующим образом:

var results = filmCategories
    .SelectMany(category => category.Films)
    .Where(film => 
        searchTerms.All(term =>
            film.ToLower().Split(' ')
            .Any(word =>            
                word.StartsWith(term))));
0

Очень маловероятно, что производительность будет проблемой, если в ваших списках нет миллионов элементов. Вы всегда должны измерять фактическую производительность использования, прежде чем пытаться оптимизировать.

В любом случае ваш код упрощается до этого:

public List<ValidAndCompletedModel> Filter(List<string> searchTerms)
{
    return (
        from searchTerm in searchTerms
        from d in ValidAndCompleted
        where d.EnglishTranslationTerms.Any(sl => sl.Contains(searchTerm))
        where d.EnglishTranslationTerms.Any(sl => sl.StartsWith(searchTerm))
        select d)
            .Distinct()
            .ToList();
}

Это не кажется слишком плохим. Единственное, что выделяется, это то, что вы возвращаете только те элементы, которые "содержат" и "начинают с" термины перевода, но это означает, что вы возвращаете только те элементы, которые "начинаются с", и игнорируют все, что "содержит". Это может потребоваться переосмысление.

Теперь вы можете пойти дальше и упростить свой код:

public List<ValidAndCompletedModel> Filter(List<string> searchTerms)
{
    return (
        from searchTerm in searchTerms
        from d in ValidAndCompleted
        from sl in d.EnglishTranslationTerms
        where sl.StartsWith(searchTerm)
        select d)
            .Distinct()
            .ToList();
}

В качестве альтернативы вы можете полностью исключить вызов .Distinct() (при отсутствии дубликатов в списке ValidAndCompleted), выполнив следующие действия:

public List<ValidAndCompletedModel> Filter2(List<string> searchTerms)
{
    return ValidAndCompleted
        .Where(d => (
            from searchTerm in searchTerms
            from sl in d.EnglishTranslationTerms
            where sl.StartsWith(searchTerm)
            select d).Any())
        .ToList();
}
  • 0
    Для полноты не забудьте выполнить поиск без учета регистра. Похоже, это то, что запросил OP.
  • 0
    @DavidG - я только что перевел код. Но для OP было бы легко сбросить .ToLowerInvariant() по мере необходимости.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню