Як використовувати LINQ Contains (рядок []) замість Містить (рядок)


103

У мене виникло одне велике запитання.

У мене з'явився запит на 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[]. Я хотів би дізнатися як, але будь-хто, хто готовий вказати мені в правильному напрямку?

EDIT: uid також буде числом. Ось чому він перетворюється на рядок.

Допомогти кому?


Потрібно уточнити, чи може виглядати uid і що вважатиметься за збіг.
Джеймс Курран

3
Приклад було б непогано. Мені це здається, що запитання задає UID на зразок: CA1FAB689C33 і масив на зразок: {"42", "2259", "CA"}
Thomas Bratt

3
Протилежний має більше сенсу: рядок []. Містить (xx.uid)
майкінотор

Відповіді:


87

spoulson є це майже вірно, але вам потрібно створити List<string>з string[]перших. Насправді, a List<int>було б краще, якщо і uid int. List<T>опори Contains(). Це uid.ToString().Contains(string[])означатиме, що uid як рядок містить усі значення масиву як підрядку ??? Навіть якби ви написали метод розширення, відчуття цього було б неправильним.

[EDIT]

Якщо ви не змінили його і не написали для того, string[]як демонструє Мітч Пшеничний, тоді ви просто зможете пропустити крок конверсії.

[ENDEDIT]

Ось що ви хочете, якщо ви не використовуєте метод розширення (якщо ви вже не маєте колекції потенційних узі як ints - тоді просто використовуйте List<int>()замість цього). Для цього використовується синтаксис ланцюгового методу, який, на мою думку, є більш чистим, і робить перетворення в int, щоб забезпечити можливість запиту використовувати більше постачальників.

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

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

Дякую. Це була правильна відповідь ... Ще одна думка? Скажімо, масив масивів - це також запит linq. Будь-яким способом ви могли звести обидва твердження лише до одного запиту з бази даних?
SpoiledTechie.com

4
Відповідно до MSDN, рядок [] реалізує IEnumerable <T>, який має метод Contains. Тому перетворювати масив в IList <T> не потрібно. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson

Останній .ToString () видає для мене помилки. Зокрема, LINQ для Entities не розпізнає метод 'System.String ToString ()', і цей метод не можна перекласти в вираз магазину .... Після його видалення лямбда працював на мене.
Сем Штанге

Мені це подобається, це так просто, що я його ніколи не пам’ятаю.
Олай

@SamStange - одна з проблем з LINQ полягає в тому, що варіантів так багато, і абстракція "нещільна", іноді потрібно знати, який варіант ви використовуєте, щоб правильно побудувати запит. Як написано, це працювало б для об'єктів LINQ (і може бути LINQ для SQL). Для EF, ви зробите це навпаки і побудуєте колекцію в пам'яті, List<int>а замість цього пропустіть ToStringвиклик.
tvanfosson

36

Якщо ви справді хочете копіювати 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 Я розумію, що це старе (іш), але (як згадував Грег), ви не хочете ", а також", а не "або ще" в цьому умовному?
Тієсон Т.

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

20

Спробуйте наступне.

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

2
Я дуже хочу, щоб люди залишали коментар, коли вони вас відзначають. Тим більше, що відповідь, яку я надавав, на 100% правильна.
JaredPar

Це був не я, але чи не все () повертає просто bool із зазначенням того, де всі елементи відповідають умові? І ініціалізація 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 Містить (цей рядок [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; }
Джеймс Курран

5
string [] реалізує IEnumerable <string>, тому він вже має метод розширення Contains (string). Чому ми повторюємо це?
Лукас

2

Це пізня відповідь, але я вважаю, що вона все ще корисна .
Я створив пакет NgetNye.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);

Або виконайте 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

Сподіваюсь, це допоможе майбутнім відвідувачам


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

Те саме, що "там, де stringArray.Contains (xx.uid.ToString ())", не потрібно обертати її у запиті
Lucas

0

Тож я правильно припускаю, що uid - це унікальний ідентифікатор (Guid)? Це лише приклад можливого сценарію чи ви справді намагаєтесь знайти орієнтир, який відповідає масиву рядків?

Якщо це правда, ви можете по-справжньому переосмислити весь цей підхід, це здається дійсно поганою ідеєю. Напевно, ви, мабуть, намагатиметеся відповідати керівництву та керівництву

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

Я, чесно кажучи, не можу уявити сценарій, коли зіставлення рядкового масиву з використанням "містить" до вмісту Керівництва було б гарною ідеєю. З одного боку, Contains () не гарантує порядок номерів у Посібнику, щоб ви могли потенційно відповідати декільком елементам. Не кажучи вже про порівняння посібників таким чином, це буде набагато повільніше, ніж просто робити це безпосередньо.


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;

Моє оригінальне повідомлення наступне:

Як робити реверс? Я хочу зробити щось подібне в рамках сутності.

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"

використання методу містить було б занадто складно, щоб зробити це тим, що ми робимо, це перетин, і для цього існує метод, який називається перетином

приклад з msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

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

Я думаю, що те, що потрібно, легко вирішується перехрестям


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

Ми, приходьте до СО. Будь ласка, не дайте відповідей "тільки для коду". Чи можете ви додати трохи пояснень, як це вирішує проблему, і це вже не охоплено іншими 21 відповідями?
болото-хитання

-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.