Jak używać LINQ Contains (string []) zamiast Contains (string)


103

Mam jedno ważne pytanie.

Otrzymałem zapytanie linq, które po prostu wygląda tak:

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

Wartości string[]tablicy byłyby liczbami takimi jak (1,45,20,10 itd.)

wartość domyślna .Containsto .Contains(string).

Muszę to zrobić zamiast tego: .Contains(string[])...

EDYCJA: Jeden użytkownik zasugerował napisanie klasy rozszerzenia dla string[]. Chciałbym się dowiedzieć, jak to zrobić, ale czy ktoś chce mi wskazać właściwy kierunek?

EDYCJA: UID byłby również liczbą. Dlatego jest konwertowany na ciąg.

Pomóż komuś?


Musisz wyjaśnić, jak może wyglądać uid i co będzie uważane za dopasowanie.
James Curran,

3
Przykład byłby fajny. Wydaje mi się, że pytanie dotyczy identyfikatora UID, takiego jak: CA1FAB689C33 i tablica w stylu: {"42", "2259", "CA"}
Thomas Bratt

3
Naprzeciwko ma więcej sensu: string []. Zawiera (xx.uid)
majkinetor

Odpowiedzi:


87

spoulson ma go prawie w porządku, ale trzeba stworzyć List<string>od string[]początku. Właściwie List<int>byłoby lepiej, gdyby uid też był int. List<T>obsługuje Contains(). Zrobienie uid.ToString().Contains(string[])tego oznaczałoby, że uid jako łańcuch zawiera wszystkie wartości tablicy jako podłańcuch ??? Nawet jeśli napiszesz metodę rozszerzenia, jej znaczenie będzie nieprawidłowe.

[EDYTOWAĆ]

Jeśli nie zmienisz tego i nie napisałeś, string[]jak pokazuje Mitch Wheat, możesz po prostu pominąć krok konwersji.

[ENDEDIT]

Oto, czego chcesz, jeśli nie wykonasz metody rozszerzenia (chyba że masz już kolekcję potencjalnych uidów jako ints - po prostu użyj List<int>()zamiast tego). Używa to składni metody połączonej, która moim zdaniem jest czystsza, i dokonuje konwersji na int, aby upewnić się, że zapytanie może być używane z większą liczbą dostawców.

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

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

Dziękuję Ci. To była właściwa odpowiedź ... Jeszcze jedna myśl? Powiedzmy, że arrayuids jest również zapytaniem linq. Czy w jakiś sposób można sprowadzić obie instrukcje do tylko jednego zapytania z bazy danych?
SpoiledTechie.com

4
Zgodnie z MSDN string [] implementuje IEnumerable <T>, który ma metodę Contains. Dlatego nie jest konieczne konwertowanie tablicy na IList <T>. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson

Ostatnia .ToString () zgłasza mi błędy. Mówiąc dokładniej, LINQ to Entities nie rozpoznaje metody „System.String ToString ()”, a tej metody nie można przetłumaczyć na wyrażenie sklepu .... Po jej usunięciu lambda zadziałała.
Sam Stange

Uwielbiam to, to takie proste, że nigdy tego nie pamiętam.
Olaj

@SamStange - jednym z problemów z LINQ jest to, że istnieje tak wiele wariantów, a abstrakcja jest „nieszczelna”, że czasami trzeba wiedzieć, którego wariantu używasz, aby poprawnie skonstruować zapytanie. Jak napisano, działałoby to dla obiektów LINQ to (i może to być LINQ to SQL). W przypadku EF zrobiłbyś to na odwrót i List<int>zamiast tego utworzył kolekcję w pamięci i pomiń ToStringwywołanie.
tvanfosson

36

Jeśli naprawdę chcesz powielić zawartość , ale w przypadku tablicy, oto metoda rozszerzenia i przykładowy kod do użycia:

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, powinieneś całkowicie przesłać to do ExtensionMethod.net Dzięki za świetny kod, rozwiązał mój problem dzisiaj!
p.campbell

4
Myślę, że miałeś na myśli! String.IsNullOrEmpty (str) && values.Length> 0
Greg Bogumil

Masz rację. Zmieniłem to, chociaż nie ma to wpływu funkcjonalnego. W pracy używam takiej funkcji. Będę musiał to sprawdzić!
Jason Jackson

@JasonJackson Zdaję sobie sprawę, że to jest stare (ish), ale (jak wspomniał Greg) czy nie chcesz „a także” zamiast „lub jeszcze” w tym warunku?
Tieson T.

@TiesonT. Zarówno warunkowe „or else”, jak i „i także” zakończyłyby się tym samym wynikiem zwróconym przez funkcję; jeśli !string.IsNullOrEmpty(str)sprawdzenie przeszło pomyślnie, powodując values.Length > 0zignorowanie warunku, ale długość wartości wynosiła 0 , to przeszedłby do foreachi natychmiast przerwałby, ponieważ nie ma żadnych wpisów w tablicy, przechodzących bezpośrednio do return false.
Meowmaritus

20

Spróbuj wykonać następujące czynności.

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

2
Naprawdę chciałbym, żeby ludzie zostawili komentarz, kiedy cię zaznaczą. Zwłaszcza, że ​​udzielona przeze mnie odpowiedź jest w 100% poprawna.
JaredPar

To nie byłem ja, ale czy All () nie zwraca po prostu wartości bool wskazującej, gdzie wszystkie elementy spełniają warunek? I zainicjowanie toSearchFor do null gwarantuje NullReferenceException.
Lucas

Zredagowałem zerowy problem, aby był tym, co zamierzałem wpisać. Tak na wszystkich. To skutecznie zapewnia, że ​​wszystkie ciągi w toSearchFor są zawarte w ciągu wejściowym.
JaredPar

6
W ogóle nie rozumiem, jak to odpowiada na pytanie. Czy pytanie zmieniło się na tobie?
tvanfosson

15

LINQ w .NET 4.0 ma dla Ciebie inną opcję; metoda .Any ();

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

1
Świetna odpowiedź, wyrażenia z Any()i All()metody są tak proste :) mogę używać t => words.All(w => t.Title.Contains(w)).
alkohol jest zły

7

Lub jeśli masz już dane na liście i wolisz inny format 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

Co powiesz na:

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

NotSupportedException: operatory porównania nie są obsługiwane dla typu „System.String []”. Dziękujemy, ale spróbować ponownie?
SpoiledTechie.com,

+1, jeśli faktycznie tego chcą. Z pytania nie wynika jasno.
Lucas

2

To jest przykład jednego ze sposobów pisania metody rozszerzającej (uwaga: nie użyłbym tego do bardzo dużych tablic; inna struktura danych byłaby bardziej odpowiednia ...):

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
byłoby to identyczne z public static bool Contains (ten ciąg [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; }
James Curran,

5
string [] implementuje IEnumerable <string>, więc ma już metodę rozszerzenia Contains (string). Dlaczego ponownie to wdrażamy?
Lucas

2

To późna odpowiedź, ale uważam, że jest nadal przydatna .
Utworzyłem pakiet nuget NinjaNye.SearchExtension , który może pomóc rozwiązać ten właśnie problem .:

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

Możesz również przeszukać wiele właściwości ciągu

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

Lub wykonaj polecenie RankedSearchktóre zwraca, IQueryable<IRanked<T>>które po prostu zawiera właściwość pokazującą, ile razy pojawiły się wyszukiwane hasła:

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

Istnieje obszerniejszy przewodnik na stronie projektów GitHub: https://github.com/ninjanye/SearchExtensions

Mam nadzieję, że pomoże to przyszłym odwiedzającym


1
Tak, został specjalnie zbudowany pod
kątem platformy

1
Czy mogę tego również używać z metodą .Where ()?
Hamza Khanzada

Tak, działa dalej IQueryablei IEnumerable- po prostu bądź z dala, jeśli połączysz go z IEnumerable, będzie działał w pamięci, zamiast budować zapytanie i wysyłać je do źródła
NinjaNye

2

Metoda rozszerzenia Linq. Będzie działać z każdym obiektem IEnumerable:

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

Stosowanie:

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

bool Array2ItemsInArray1 = List1.ContainsAny(List2);

1

Myślę, że możesz też zrobić coś takiego.

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

To samo, co „gdzie stringArray.Contains (xx.uid.ToString ())”, nie ma potrzeby zawijania go w zapytaniu
Lucas,

0

Czy więc poprawnie zakładam, że uid jest unikalnym identyfikatorem (Guid)? Czy to tylko przykład możliwego scenariusza, czy naprawdę próbujesz znaleźć guid, który pasuje do tablicy ciągów?

Jeśli to prawda, możesz naprawdę przemyśleć całe podejście, wydaje się to naprawdę złym pomysłem. Prawdopodobnie powinieneś próbować dopasować Guid do Guid

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

Szczerze mówiąc, nie wyobrażam sobie scenariusza, w którym dopasowanie tablicy ciągów przy użyciu „zawiera” do zawartości Guid byłoby dobrym pomysłem. Po pierwsze, Contains () nie gwarantuje kolejności liczb w Guid, więc możesz potencjalnie dopasować wiele elementów. Nie wspominając o tym, że porównywanie przewodników w ten sposób byłoby o wiele wolniejsze niż robienie tego bezpośrednio.


0

Powinieneś napisać to na odwrót, sprawdzając, czy lista identyfikatorów uprzywilejowanych użytkowników zawiera identyfikator w tym wierszu tabeli:

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

LINQ zachowuje się tutaj dość jasno i konwertuje to na dobrą instrukcję 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'

który zasadniczo osadza zawartość tablicy „search” w kwerendzie sql i filtruje za pomocą słowa kluczowego „IN” w SQL.


Działa to dobrze, o ile nie masz ponad 2100 parametrów.
jpierson,

0

Udało mi się znaleźć rozwiązanie, ale nie świetne, ponieważ wymaga użycia AsEnumerable (), który zwróci wszystkie wyniki z bazy danych, na szczęście mam tylko 1k rekordów w tabeli, więc nie jest to naprawdę zauważalne, ale tutaj idzie .

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;

Mój oryginalny post jest następujący:

Jak to zrobić na odwrót? Chcę zrobić coś takiego w ramach encji.

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

Chcę znaleźć wszystkich użytkowników, w których ich FullName zawiera wszystkie elementy z „wyszukiwania”. Wypróbowałem wiele różnych sposobów, z których żaden nie zadziałał.

Ja też próbowałem

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

Ta wersja znajduje tylko te, które zawierają ostatni element w tablicy wyszukiwania.


0

Najlepszym rozwiązaniem, jakie znalazłem, było utworzenie w języku SQL funkcji wartościowanej tabelą, która generuje wyniki, takie jak:

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

Następnie wystarczy przeciągnąć funkcję do projektanta LINQ.dbml i wywołać ją tak, jak inne obiekty. LINQ zna nawet kolumny twojej przechowywanej funkcji. Wołam to tak:

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

Niesamowicie proste i naprawdę wykorzystujące moc SQL i LINQ w aplikacji ... i oczywiście możesz wygenerować dowolną funkcję o wartości tabeli, którą chcesz, aby uzyskać te same efekty!


0

Uważam, że to, co naprawdę chcesz zrobić, to: wyobraźmy sobie scenariusz, w którym masz dwie bazy danych i mają one wspólną tabelę produktów I chcesz wybrać produkty z tabeli „A”, która ma wspólny identyfikator z „B”

użycie metody zawiera byłoby zbyt skomplikowane, aby to zrobić, to co robimy jest skrzyżowaniem i istnieje do tego metoda zwana przecięciem

przykład z msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

int [] liczby = (0, 2, 4, 5, 6, 8, 9); int [] liczbyB = (1, 3, 5, 7, 8); var = commonNumbers numbersA.Intersect (numbersB);

Myślę, że to, czego potrzebujesz, można łatwo rozwiązać za pomocą przecięcia


0

Sprawdź tę metodę rozszerzenia:

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

Próbować:

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

Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
Francesco Menzani

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

Przychodzimy do SO. Nie udzielaj odpowiedzi „tylko kod”. Czy mógłbyś dodać jakieś wyjaśnienie, jak to rozwiązuje problem i nie zostało już uwzględnione w pozostałych 21 odpowiedziach?
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()) 
  }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.