Конвертировать LINQ (Entity Framework), вложенный Any, чтобы принять N параметров - PullRequest
0 голосов
/ 03 мая 2018

Требование: проверьте, существует ли список последовательных слов в наборе данных. Если это так, верните логическое значение, чтобы показать успех.

Вот мой код с модульными тестами ( обратите внимание, что это только пример кода - DataSet будет с Entity Framework, а не просто списком ):

[Test]
public void PhraseSearch()
{
    var DataSet = new List<Word>
                    {
                        new Word { Text = "First", Sequence = 0 },
                        new Word { Text = "Second", Sequence = 1 },
                        new Word { Text = "Third", Sequence = 2 },
                        new Word { Text = "Forth", Sequence = 3 },
                        new Word { Text = "Five", Sequence = 4 }
                    };

    var goodSearch = new string[]{ "First", "Second", "Third" };
    var badSearch = new string[] { "First", "NOTFOUND", "Third" };

    // successful test for 2 words
    var result = DataSet.Any(wrd1 => wrd1.Text == goodSearch[0] && DataSet.Any(wrd2 => wrd2.Text == goodSearch[1] && wrd2.Sequence == wrd1.Sequence + 1));
    Assert.That(result, Is.True);

     result = DataSet.Any(wrd1 => wrd1.Text == badSearch[0] && 
                DataSet.Any(wrd2 => wrd2.Text == badSearch[1] && wrd2.Sequence == wrd1.Sequence + 1));

    // successful test for 2 words that don't match the data
    Assert.That(result, Is.False);

    // successful test for 3 words
    result = DataSet.Any(wrd1 => wrd1.Text == goodSearch[0] && 
                               DataSet.Any(wrd2 => wrd2.Text == goodSearch[1] && wrd2.Sequence == wrd1.Sequence + 1 && 
                                                 DataSet.Any(wrd3 => wrd3.Text == goodSearch[2] && wrd3.Sequence == wrd2.Sequence + 1)));
    Assert.That(result, Is.True);

    // test for N words
    result = .....
}

Я хочу расширить код Linq, чтобы сделать N слов, но я не уверен, как это сделать с Linq в Entity Framework, я склоняюсь к жестко закодированному методу для каждого числа слов, но это кажется действительно вонючим .

Ответы [ 3 ]

0 голосов
/ 03 мая 2018

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

public static bool ContainsSequence<T>(this IEnumerable<T> seq, IEnumerable<T> subSeq, EqualityComparer<T> comparer = null)
{
    if (comparer == null)
        comparer = EqualityComparer<T>.Default;
    IList<T> list = subSeq as IList<T> ?? new List<T>(subSeq);
    IEnumerable<int> allIndexes = seq.AllIndexesOf(list.First(), comparer);
    foreach (int index in allIndexes)
    {
        bool containsSequence = seq.Skip(index).Take(list.Count).SequenceEqual(list, comparer);
        if (containsSequence)
            return true;
    }
    return false;
}

Использование этого простого расширения для поиска всех индексов:

public static IEnumerable<int> AllIndexesOf<T>(this IEnumerable<T> seq, T itemToFind, EqualityComparer<T> comparer = null)
{
    if (comparer == null)
        comparer = EqualityComparer<T>.Default;
    int index = 0;
    foreach (T item in seq)
    {
        if (comparer.Equals(itemToFind, item))
            yield return index;
        index++;
    }
}

Теперь оставшаяся проверка проста:

bool containsSubseq = DataSet.OrderBy(x => x.Sequence).Select(x => x.Text)
   .ContainsSequence(goodSearch);

Однако это работает для Linq-To-Objects, а не для провайдеров LINQ, управляемых базой данных (слишком поздно).

0 голосов
/ 04 мая 2018

Мне нравятся запросы подмножества, но я закончил тем, что написал рекурсивное любое утверждение, чтобы решить это как показано ниже:

  public void PhraseSearch()
    {
        var DataSet = new List<Word>
                        {
                            new Word { Text = "First", Sequence = 0 },
                            new Word { Text = "Second", Sequence = 1 },
                            new Word { Text = "Third", Sequence = 2 },
                            new Word { Text = "Forth", Sequence = 3 },
                            new Word { Text = "Five", Sequence = 4 }
                        };

        var goodSearch1 = new[] { "Second" };
        var goodSearch2 = new[] { "First", "Second"};
        var goodSearch3 = new[] {  "Second", "Third", "Forth" };
        var badSearch = new[] { "First", "NOTFOUND", "Third" };

        // successful test for 1 word
        int idxTosearch = 0;
        var result = DataSet.Any(wrd => wrd.Text == goodSearch1[idxTosearch] && NextAny(goodSearch1,idxTosearch + 1,DataSet, wrd));
        Assert.That(result, Is.True);


        //reset counter
        idxTosearch = 0;
        result = DataSet.Any(wrd => wrd.Text == goodSearch2[0] && NextAny(goodSearch2, idxTosearch + 1, DataSet, wrd));

        // successful test for 2 words 
        Assert.That(result, Is.True);

        //reset counter
        idxTosearch = 0;
        // successful test for 3 words
        result = DataSet.Any(wrd => wrd.Text == goodSearch3[0] && NextAny(goodSearch3, idxTosearch + 1, DataSet, wrd));
        Assert.That(result, Is.True);

        // test for bad  words
        //reset counter
        idxTosearch = 0;
        result = DataSet.Any(wrd => wrd.Text == badSearch[0] && NextAny(badSearch, idxTosearch + 1, DataSet, wrd));
        Assert.That(result, Is.False);


    }

 private static bool NextAny(string[] phraseArray, int idxToSearch, List<Word> DataSet, Word previousWord)
    {

        if (idxToSearch  == phraseArray.Length)
        {
            return true;
        }

        return allMatches.Any(wrd => wrd.Text == phraseArray[idxToSearch] && wrd.Sequence == previousWord.Sequence + 1 && NextAny(phraseArray, idxToSearch + 1, DataSet, wrd));
    }
0 голосов
/ 03 мая 2018

Попробуйте найти подмножество:

public void PhraseSearch()
{
    var dataSet = new[] { "First", "Second", "Third", "Forth", "Five", };

    var goodSearch = new[] { "First", "Second", "Third" };
    var badSearch = new[] { "First", "NOTFOUND", "Third" };

    Console.WriteLine(SubsequenceMatch(dataSet, new[] { "First", "Second", "Third" }));
    Console.WriteLine(SubsequenceMatch(dataSet, new[] { "First", "NOTFOUND", "Third" }));   
    Console.WriteLine(SubsequenceMatch(dataSet, new[] { "Second", "Third", "Forth" }));
    Console.WriteLine(SubsequenceMatch(dataSet, new[] { "Second", "Third", "Forth", "Five" }));
}

public bool SubsequenceMatch<T>(T[] source, T[] match)
{
    return
        Enumerable
            .Range(0, source.Count() - match.Count() + 1)
            .Any(n => source.Skip(n).Take(match.Count()).SequenceEqual(match));
}

Это дает:

True
False
True
True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...