Как использовать LINQ Contains (string []) вместо Contains (string)


102

У меня есть один большой вопрос.

Я получил запрос linq, чтобы выразить это просто так:

from xx in table
where xx.uid.ToString().Contains(string[])
select xx

Значения string[]массива будут числами вроде (1,45,20,10 и т. Д.)

По умолчанию .Containsиспользуется .Contains(string).

Мне нужно это сделать вместо этого: .Contains(string[])...

EDIT: один пользователь предложил написать класс расширения для string[]. Я хотел бы узнать, как это сделать, но кто-нибудь хочет указать мне в правильном направлении?

РЕДАКТИРОВАТЬ: uid также будет числом. Вот почему он конвертируется в строку.

Кому-нибудь помочь?


Вам необходимо уточнить, как будет выглядеть uid и что будет считаться совпадением.
Джеймс Карран,

3
Было бы неплохо привести пример. Мне кажется, что вопрос задает UID, например: CA1FAB689C33, и массив, например: {"42", "2259", "CA"}
Томас Братт,

3
Противоположное имеет больше смысла: string []. Contains (xx.uid)
majkinetor 04

Ответы:


86

spoulson есть это почти верно, но вам нужно создать List<string>из string[]первых. На самом деле List<int>было бы лучше , если UID также int. List<T>поддерживает Contains(). Это uid.ToString().Contains(string[])будет означать, что uid в виде строки содержит все значения массива в виде подстроки ??? Даже если бы вы написали метод расширения, смысл этого был бы неправильным.

[РЕДАКТИРОВАТЬ]

Если вы не изменили его и не написали, string[]как демонстрирует Митч Уит, вы просто сможете пропустить этап преобразования.

[ENDEDIT]

Вот что вы хотите, если вы не используете метод расширения (если у вас еще нет коллекции потенциальных uid в виде целых чисел - просто используйте List<int>()вместо этого). При этом используется синтаксис цепочечного метода, который, на мой взгляд, более понятный, и выполняется преобразование в int, чтобы запрос можно было использовать с большим количеством поставщиков.

var uids = arrayofuids.Select(id => int.Parse(id)).ToList();

var selected = table.Where(t => uids.Contains(t.uid));

Спасибо. Это был правильный ответ ... Еще одна мысль? Допустим, arrayuids также является запросом linq. В любом случае, вы могли бы свести оба оператора к одному запросу из базы данных?
SpoiledTechie.com

4
Согласно MSDN, string [] реализует IEnumerable <T>, который имеет метод Contains. Следовательно, нет необходимости преобразовывать массив в IList <T>. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson

Последний .ToString () вызывает у меня ошибки. В частности, LINQ to Entities не распознает метод System.String ToString (), и этот метод не может быть преобразован в выражение хранилища .... После его удаления лямбда работала у меня.
Сэм Стэндж,

Мне это нравится, это так просто, что я никогда этого не помню.
Olaj

@SamStange - одна проблема с LINQ заключается в том, что существует так много вариантов, а абстракция «дырявая», иногда вам нужно знать, какой вариант вы используете, чтобы правильно построить запрос. Как написано, это будет работать для LINQ to objects (и может быть LINQ to SQL). Для EF вы должны сделать это наоборот и List<int>вместо этого создать коллекцию в памяти как и пропустить ToStringвызов.
tvanfosson

37

Если вы действительно хотите реплицировать Contains , но для массива, вот метод расширения и пример кода для использования:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ContainsAnyThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            string testValue = "123345789";

            //will print true
            Console.WriteLine(testValue.ContainsAny("123", "987", "554")); 

            //but so will this also print true
            Console.WriteLine(testValue.ContainsAny("1", "987", "554"));
            Console.ReadKey();

        }
    }

    public static class StringExtensions
    {
        public static bool ContainsAny(this string str, params string[] values)
        {
            if (!string.IsNullOrEmpty(str) || values.Length > 0)
            {
                foreach (string value in values)
                {
                    if(str.Contains(value))
                        return true;
                }
            }

            return false;
        }
    }
}

2
+1 @Jason, вы должны полностью отправить это на ExtensionMethod.net Спасибо за отличный код, сегодня он решил мою проблему!
p.campbell

4
Я думаю, вы имели в виду! String.IsNullOrEmpty (str) && values.Length> 0
Грег Богумил

Ты прав. Я его поменял, но функционального влияния не оказывает. Я использую такую ​​функцию на работе. Придется это проверить!
Джейсон Джексон

@JasonJackson Я понимаю, что это старый (иш), но (как сказал Грег) разве вы не хотите, чтобы в этом условном выражении было «а также», а не «или еще»?
Tieson T.

@TiesonT. И «or else», и «and also» в конечном итоге дадут один и тот же результат, возвращаемый функцией; если !string.IsNullOrEmpty(str)проверка прошла успешно, в результате чего values.Length > 0условие игнорировалось, но длина Values ​​была равна 0 , тогда он перешел бы в, foreachа затем немедленно прервался бы, потому что в массиве нет записей, переходя непосредственно в return false.
Meowmaritus

20

Попробуйте следующее.

string input = "someString";
string[] toSearchFor = GetSearchStrings();
var containsAll = toSearchFor.All(x => input.Contains(x));

2
Я действительно хочу, чтобы люди оставляли комментарии, когда отмечали вас. Тем более что ответ, который я дал, на 100% правильный.
JaredPar

Это был не я, но разве All () не возвращает просто логическое значение, указывающее, где все элементы соответствуют условию? И инициализация toSearchFor нулевым значением гарантирует исключение NullReferenceException.
Лукас,

Я отредактировал нулевую проблему так, чтобы она соответствовала тому, что собиралась ввести. Да на всех. Это эффективно гарантирует, что все строки в toSearchFor содержатся во входной строке.
JaredPar

6
Я вообще не понимаю, как это отвечает на вопрос. Вопрос изменился на вас?
tvanfosson

15

LINQ в .NET 4.0 предлагает вам еще один вариант; метод .Any ();

string[] values = new[] { "1", "2", "3" };
string data = "some string 1";
bool containsAny = values.Any(data.Contains);

1
Отличный ответ, выражения с Any()и All()методы настолько просты :) Я могу использовать t => words.All(w => t.Title.Contains(w)).
алкоголь это зло

7

Или, если у вас уже есть данные в списке и вы предпочитаете другой формат Linq :)

List<string> uids = new List<string>(){"1", "45", "20", "10"};
List<user> table = GetDataFromSomewhere();

List<user> newTable = table.Where(xx => uids.Contains(xx.uid)).ToList();

3

Как насчет:

from xx in table
where stringarray.Contains(xx.uid.ToString())
select xx

NotSupportedException: операторы сравнения не поддерживаются для типа System.String []. Спасибо, но попробуйте еще раз?
SpoiledTechie.com

+1, если они действительно этого хотят. Не очень понятно из вопроса.
Лукас,

2

Это пример одного из способов написания метода расширения (примечание: я бы не стал использовать его для очень больших массивов; более подходящей была бы другая структура данных ...):

namespace StringExtensionMethods
{
    public static class StringExtension
    {
        public static bool Contains(this string[] stringarray, string pat)
        {
            bool result = false;

            foreach (string s in stringarray)
            {
                if (s == pat)
                {
                    result = true;
                    break;
                }
            }

            return result;
        }
    }
}

1
это было бы идентично общедоступному статическому типу bool Contains (this string [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; }
Джеймс Карран,

5
string [] реализует IEnumerable <string>, поэтому у него уже есть метод расширения Contains (string). Зачем мы это реализуем заново?
Лукас,

2

Это поздний ответ, но я считаю, что он все еще полезен .
Я создал пакет nuget NinjaNye.SearchExtension , который может помочь решить эту самую проблему:

string[] terms = new[]{"search", "term", "collection"};
var result = context.Table.Search(terms, x => x.Name);

Вы также можете искать несколько строковых свойств

var result = context.Table.Search(terms, x => x.Name, p.Description);

Или выполните команду RankedSearchwhich return, IQueryable<IRanked<T>>которая просто включает свойство, показывающее, сколько раз появлялись условия поиска:

//Perform search and rank results by the most hits
var result = context.Table.RankedSearch(terms, x => x.Name, x.Description)
                     .OrderByDescending(r = r.Hits);

На странице GitHub по проектам есть более подробное руководство: https://github.com/ninjanye/SearchExtensions

Надеюсь, это поможет будущим посетителям


1
Да, он был специально создан для Entity Framework
NinjaNye

1
Могу ли я использовать это и с методом .Where ()?
Хамза Ханзада,

Да, он работает, IQueryableи IEnumerable- только будьте в виду, что если вы подключите его к IEnumerable, он будет работать в памяти, а не создавать запрос и отправлять его источнику
NinjaNye

2

Метод расширения Linq. Будет работать с любым объектом IEnumerable:

    public static bool ContainsAny<T>(this IEnumerable<T> Collection, IEnumerable<T> Values)
    {
        return Collection.Any(x=> Values.Contains(x));
    }

Использование:

string[] Array1 = {"1", "2"};
string[] Array2 = {"2", "4"};

bool Array2ItemsInArray1 = List1.ContainsAny(List2);

1

Я считаю, что вы могли бы сделать что-то подобное.

from xx in table
where (from yy in string[] 
       select yy).Contains(xx.uid.ToString())
select xx

То же, что и «where stringArray.Contains (xx.uid.ToString ())», нет необходимости заключать его в запрос
Лукас,

0

Итак, правильно ли я предполагаю, что uid - это уникальный идентификатор (Guid)? Это просто пример возможного сценария или вы действительно пытаетесь найти guid, который соответствует массиву строк?

Если это правда, вы можете действительно переосмыслить весь этот подход, это кажется действительно плохой идеей. Вероятно, вам стоит попытаться сопоставить Guid с Guid

Guid id = new Guid(uid);
var query = from xx in table
            where xx.uid == id
            select xx;

Честно говоря, я не могу представить себе сценарий, при котором сопоставление массива строк с использованием "contains" с содержимым Guid было бы хорошей идеей. Во-первых, Contains () не гарантирует порядок чисел в Guid, поэтому вы потенциально можете сопоставить несколько элементов. Не говоря уже о том, чтобы сравнивать направляющие таким образом, было бы намного медленнее, чем просто делать это напрямую.


0

Вы должны написать это наоборот, проверив, что ваш список идентификаторов привилегированных пользователей содержит идентификатор в этой строке таблицы:

string[] search = new string[] { "2", "3" };
var result = from x in xx where search.Contains(x.uid.ToString()) select x;

LINQ ведет себя здесь довольно ярко и преобразует его в хороший оператор SQL:

sp_executesql N'SELECT [t0].[uid]
FROM [dbo].[xx] AS [t0]
WHERE (CONVERT(NVarChar,[t0].[uid]))
IN (@p0, @p1)',N'@p0 nvarchar(1),
@p1 nvarchar(1)',@p0=N'2',@p1=N'3'

который в основном включает содержимое массива search в запрос sql и выполняет фильтрацию с ключевым словом IN в SQL.


Это отлично работает, если у вас не более 2100 параметров.
jpierson

0

Мне удалось найти решение, но не лучшее, поскольку для этого требуется использовать AsEnumerable (), который будет возвращать все результаты из БД, к счастью, у меня есть только 1k записей в таблице, поэтому это не очень заметно, но вот .

var users = from u in (from u in ctx.Users
                       where u.Mod_Status != "D"
                       select u).AsEnumerable()
            where ar.All(n => u.FullName.IndexOf(n,
                        StringComparison.InvariantCultureIgnoreCase) >= 0)
            select u;

Мой исходный пост следует:

Как сделать наоборот? Я хочу сделать что-то вроде следующего в Entity framework.

string[] search = new string[] { "John", "Doe" };
var users = from u in ctx.Users
            from s in search
           where u.FullName.Contains(s)
          select u;

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

Я также пробовал

var users = from u in ctx.Users select u;
foreach (string s in search) {
    users = users.Where(u => u.FullName.Contains(s));
}

Эта версия находит только те, которые содержат последний элемент в массиве поиска.


0

Лучшее решение, которое я нашел, - это создать в SQL табличную функцию, которая дает результаты, например:

CREATE function [dbo].[getMatches](@textStr nvarchar(50)) returns @MatchTbl table(
Fullname nvarchar(50) null,
ID nvarchar(50) null
)
as begin
declare @SearchStr nvarchar(50);
set @SearchStr = '%' + @textStr + '%';
insert into @MatchTbl 
select (LName + ', ' + FName + ' ' + MName) AS FullName, ID = ID from employees where LName like @SearchStr;
return;
end

GO

select * from dbo.getMatches('j')

Затем вы просто перетаскиваете функцию в свой конструктор LINQ.dbml и вызываете ее так же, как и другие объекты. LINQ даже знает столбцы вашей сохраненной функции. Я называю это так:

Dim db As New NobleLINQ
Dim LNameSearch As String = txt_searchLName.Text
Dim hlink As HyperLink

For Each ee In db.getMatches(LNameSearch)
   hlink = New HyperLink With {.Text = ee.Fullname & "<br />", .NavigateUrl = "?ID=" & ee.ID}
   pnl_results.Controls.Add(hlink)
Next

Невероятно простой и действительно раскрывает всю мощь SQL и LINQ в приложении ... и вы, конечно же, можете сгенерировать любую функцию с табличным значением, какую захотите, для тех же эффектов!


0

Я считаю, что вы действительно хотите: давайте представим сценарий, у вас есть две базы данных и у них есть общая таблица продуктов. И вы хотите выбрать продукты из таблицы «A», которая имеет общий идентификатор с «B».

использование метода contains было бы слишком сложно сделать это то, что мы делаем, это пересечение, и для этого есть метод, называемый пересечением

пример из msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

int [] числа = (0, 2, 4, 5, 6, 8, 9); int [] numbersB = (1, 3, 5, 7, 8); var = commonNumbers numbersA.Intersect (numbersB);

Я думаю то, что вам нужно, легко решается с помощью пересечения


0

Проверьте этот метод расширения:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ContainsAnyProgram
{
    class Program
    {
        static void Main(string[] args)
        {
            const string iphoneAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like...";

            var majorAgents = new[] { "iPhone", "Android", "iPad" };
            var minorAgents = new[] { "Blackberry", "Windows Phone" };

            // true
            Console.WriteLine(iphoneAgent.ContainsAny(majorAgents));

            // false
            Console.WriteLine(iphoneAgent.ContainsAny(minorAgents));
            Console.ReadKey();
        }
    }

    public static class StringExtensions
    {
        /// <summary>
        /// Replicates Contains but for an array
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="values">The values.</param>
        /// <returns></returns>
        public static bool ContainsAny(this string str, params string[] values)
        {
            if (!string.IsNullOrEmpty(str) && values.Length > 0)
                return values.Any(str.Contains);

            return false;
        }
    }
}


0

Пытаться:

var stringInput = "test";
var listOfNames = GetNames();
var result = from names in listOfNames where names.firstName.Trim().ToLower().Contains(stringInput.Trim().ToLower());
select names;

Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа.
Система

0
var SelecetdSteps = Context.FFTrakingSubCriticalSteps
             .Where(x => x.MeetingId == meetid)
             .Select(x =>    
         x.StepID  
             );

        var crtiticalsteps = Context.MT_CriticalSteps.Where(x =>x.cropid==FFT.Cropid).Select(x=>new
        {
            StepID= x.crsid,
            x.Name,
            Checked=false

        });


        var quer = from ax in crtiticalsteps
                   where (!SelecetdSteps.Contains(ax.StepID))
                   select ax;

0
        string texto = "CALCA 40";
        string[] descpart = texto.Split(' ');

        var lst = (from item in db.InvItemsMaster
                   where descpart.All(val => item.itm_desc.Contains(val))
                   select item
                    ).ToList();
        Console.WriteLine("ITM".PadRight(10) + "DESC".PadRight(50)+"EAN".PadRight(14));
        foreach(var i in lst)
        {
           

            Console.Write(i.itm_id.ToString().PadRight(10));
            Console.Write(i.itm_desc.ToString().PadRight(50));
            Console.WriteLine(i.itm_ean.ToString().PadRight(14));


        }

        Console.ReadKey();

Мы приезжаем в SO. Пожалуйста, не давайте ответов «только код». Не могли бы вы добавить объяснение, как это решает проблему и еще не покрыто другими 21 ответами?
marsh-wiggle

-1
string[] stringArray = {1,45,20,10};
from xx in table 
where stringArray.Contains(xx.uid.ToString()) 
select xx

-2
Dim stringArray() = {"Pink Floyd", "AC/DC"}
Dim inSQL = From alb In albums Where stringArray.Contains(alb.Field(Of String)("Artiste").ToString())
Select New With
  {
     .Album = alb.Field(Of String)("Album"),
     .Annee = StrReverse(alb.Field(Of Integer)("Annee").ToString()) 
  }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.