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

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

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

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

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

Значение по умолчанию для .Contains - .Contains(string).

Вместо этого мне нужно это сделать: _5 _...

РЕДАКТИРОВАТЬ: один пользователь предложил написать класс расширения для string[]. Я хотел бы узнать, как это сделать, но кто-нибудь, желающий указать мне в правильном направлении?

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

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


person SpoiledTechie.com    schedule 12.10.2008    source источник
comment
Вам необходимо уточнить, как будет выглядеть uid и что будет считаться совпадением.   -  person James Curran    schedule 12.10.2008
comment
Было бы неплохо привести пример. Мне кажется, что вопрос задает UID, например: CA1FAB689C33, и массив, например: {42, 2259, CA}   -  person Thomas Bratt    schedule 12.10.2008
comment
Противоположное имеет больше смысла: строка []. Содержит (xx.uid)   -  person majkinetor    schedule 04.03.2010


Ответы (18)


Споулсон почти прав, но сначала вам нужно создать 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));
person tvanfosson    schedule 12.10.2008
comment
Спасибо. Это был правильный ответ ... Еще одна мысль? Допустим, arrayuids также является запросом linq. В любом случае, вы могли бы свести оба оператора к одному запросу из базы данных? - person SpoiledTechie.com; 13.10.2008
comment
Согласно MSDN, string [] реализует IEnumerable ‹T›, который имеет метод Contains. Следовательно, нет необходимости преобразовывать массив в IList ‹T›. msdn.microsoft.com/en-us/library/19e6zeyy.aspx - person spoulson; 13.10.2008
comment
Последний .ToString () вызывает у меня ошибки. В частности, LINQ to Entities не распознает метод System.String ToString (), и этот метод не может быть преобразован в выражение хранилища .... После его удаления лямбда сработала для меня. - person Sam Stange; 21.09.2011
comment
Мне это нравится, это так просто, что я никогда этого не вспомню. - person Olaj; 28.09.2011
comment
@SamStange - одна проблема с LINQ заключается в том, что существует так много вариантов, а абстракция нечеткая, вам иногда нужно знать, какой вариант вы используете, чтобы правильно построить запрос. Как написано, это будет работать для LINQ to objects (и может быть LINQ to SQL). Для EF вы должны сделать это наоборот и вместо этого создать коллекцию в памяти как List<int> и пропустить вызов ToString. - person tvanfosson; 28.09.2011
comment
HashSet будет еще быстрее - person paparazzo; 26.05.2015
comment
@Blam, если только это не запрос, который конвертируется в SQL, что, я думаю, было контекстом в то время. - person tvanfosson; 26.05.2015

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

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;
        }
    }
}
person Jason Jackson    schedule 12.10.2008
comment
+1 @Jason, вам следует полностью отправить это на ExtensionMethod.net Спасибо за отличный код, сегодня он решил мою проблему! - person p.campbell; 19.11.2009
comment
Я думаю, вы имели в виду! String.IsNullOrEmpty (str) && values.Length ›0 - person Greg Bogumil; 18.06.2010
comment
Ты прав. Я его поменял, но функционального влияния не оказывает. Я использую такую ​​функцию на работе. Придется это проверить! - person Jason Jackson; 19.06.2010
comment
@JasonJackson Я понимаю, что это старый (иш), но (как упомянул Грег) разве вам не нужно, а также, а не иначе в этом условном выражении? - person Tieson T.; 06.09.2015
comment
@TiesonT. И условные выражения or else и также приведут к одному и тому же результату, возвращаемому функцией; если проверка !string.IsNullOrEmpty(str) пройдена, в результате чего условие values.Length > 0 будет проигнорировано, но длина Values ​​была 0, тогда он перешел бы в foreach, а затем немедленно прервался бы, потому что в массиве нет записей, прямо в return false. - person Meowmaritus; 01.10.2017
comment
@ GreenCat77 Это правда, но зачем вообще заходить в цикл, если в value нет элементов? - person Tieson T.; 01.10.2017
comment
Наконец, пример, показывающий, как искать строки массива в другой строке. - person nickornotto; 30.01.2020
comment
Грег Богумил прав, это должно быть && не || в операторе if. В противном случае первая проверка бессмысленна, если values не пуст. - person Trisped; 11.06.2020

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

string input = "someString";
string[] toSearchFor = GetSearchStrings();
var containsAll = toSearchFor.All(x => input.Contains(x));
person JaredPar    schedule 12.10.2008
comment
Я действительно хочу, чтобы люди оставляли комментарии, когда отмечали вас. Тем более что ответ, который я дал, верен на 100%. - person JaredPar; 13.10.2008
comment
Это был не я, но разве All () не возвращает просто логическое значение, указывающее, где все элементы соответствуют условию? И инициализация toSearchFor нулевым значением гарантирует исключение NullReferenceException. - person Lucas; 14.10.2008
comment
Я отредактировал нулевую проблему, чтобы она соответствовала тому, что собирался ввести. Да на всех. Это эффективно гарантирует, что все строки в toSearchFor содержатся во входной строке. - person JaredPar; 15.10.2008
comment
Я вообще не понимаю, как это отвечает на вопрос. Вопрос изменился на вас? - person tvanfosson; 04.02.2015

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

string[] values = new[] { "1", "2", "3" };
string data = "some string 1";
bool containsAny = values.Any(data.Contains);
person RJ Lohan    schedule 14.03.2012
comment
Отличный ответ, выражения с методами Any() и All() настолько просты :) Я могу использовать t => words.All(w => t.Title.Contains(w)). - person alcohol is evil; 12.01.2017

Или, если у вас уже есть данные в списке и вы предпочитаете другой формат 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();
person JumpingJezza    schedule 28.04.2011

Как насчет:

from xx in table
where stringarray.Contains(xx.uid.ToString())
select xx
person spoulson    schedule 12.10.2008
comment
NotSupportedException: операторы сравнения не поддерживаются для типа System.String []. Спасибо, но попробуйте еще раз? - person SpoiledTechie.com; 12.10.2008
comment
+1, если они действительно этого хотят. Не очень понятно из вопроса. - person Lucas; 14.10.2008

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

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;
        }
    }
}
person Mitch Wheat    schedule 12.10.2008
comment
это было бы идентично общедоступному статическому типу bool Contains (this string [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; } - person James Curran; 12.10.2008
comment
string [] реализует IEnumerable ‹string›, поэтому у него уже есть метод расширения Contains (string). Почему мы перерабатываем это? - person Lucas; 14.10.2008

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

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);

Или выполните RankedSearch, который возвращает 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

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

person NinjaNye    schedule 13.02.2014
comment
Да, он был специально создан для Entity Framework - person NinjaNye; 21.12.2016
comment
Могу ли я использовать это и с методом .Where ()? - person Hamza Khanzada; 29.03.2020
comment
Да, он работает с IQueryable и IEnumerable - просто имейте в виду, что если вы подключите его к IEnumerable, он будет работать в памяти, а не создавать запрос и отправлять его источнику - person NinjaNye; 31.03.2020

Метод расширения 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);
person kravits88    schedule 17.04.2014

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

from xx in table
where (from yy in string[] 
       select yy).Contains(xx.uid.ToString())
select xx
person ctrlShiftBryan    schedule 12.10.2008
comment
То же, что и stringArray.Contains (xx.uid.ToString ()), нет необходимости оборачивать его в запросе - person Lucas; 14.10.2008

Итак, правильно ли я предполагаю, что 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, поэтому вы потенциально можете сопоставить несколько элементов. Не говоря уже о том, чтобы сравнивать направляющие таким образом, было бы намного медленнее, чем просто делать это напрямую.

person justin.m.chase    schedule 12.10.2008

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

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.

person Gorkem Pacaci    schedule 19.01.2009
comment
Это отлично работает, если у вас не более 2100 параметров. - person jpierson; 30.09.2013

Мне удалось найти решение, но не лучшее, поскольку для этого требуется использование 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));
}

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

person Brett Ryan    schedule 17.07.2009

Лучшее решение, которое я нашел, - это создать в 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 в приложении ... и вы, конечно, можете сгенерировать любую функцию с табличным значением, какую захотите, для тех же эффектов!

person beauXjames    schedule 18.06.2010

Я считаю, что вы действительно хотите: давайте представим сценарий, у вас есть две базы данных и у них есть общая таблица продуктов. И вы хотите выбрать продукты из таблицы «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);

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

person Lucas Cria    schedule 04.08.2010

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

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;
        }
    }
}
person theRonny    schedule 10.08.2012

Пытаться:

var stringInput = "test";
var listOfNames = GetNames();
var result = from names in listOfNames where names.firstName.Trim().ToLower().Contains(stringInput.Trim().ToLower());
select names;
person Hedego    schedule 08.08.2015
comment
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа. - person spongebob; 08.08.2015

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