Come si usa LINQ Contains (string []) invece di Contains (string)


103

Ho una grande domanda.

Ho una query linq per dirla semplicemente come questa:

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

I valori string[]dell'array sarebbero numeri come (1,45,20,10, ecc ...)

il valore predefinito per .Containsè .Contains(string).

Ne ho bisogno per fare questo invece: .Contains(string[])...

EDIT: un utente ha suggerito di scrivere una classe di estensione per string[]. Mi piacerebbe imparare come, ma qualcuno è disposto a indicarmi la giusta direzione?

EDIT: L'uid sarebbe anche un numero. Ecco perché viene convertito in una stringa.

Aiutare qualcuno?


È necessario chiarire che aspetto potrebbe avere un uid e cosa sarebbe considerato una corrispondenza.
James Curran

3
Un esempio sarebbe carino. Mi sembra che la domanda chieda un UID come: CA1FAB689C33 e l'array come: {"42", "2259", "CA"}
Thomas Bratt

3
L'opposto ha più senso: string []. Contiene (xx.uid)
majkinetor

Risposte:


87

spoulson ha quasi ragione, ma è necessario creare una List<string>da string[]prima. In realtà List<int>sarebbe meglio se lo fosse anche uid int. List<T>supporti Contains(). Ciò uid.ToString().Contains(string[])implicherebbe che l'uid come stringa contiene tutti i valori dell'array come sottostringa ??? Anche se scrivessi il metodo di estensione, il senso sarebbe sbagliato.

[MODIFICARE]

A meno che tu non l'abbia cambiato e l'abbia scritto per string[]come dimostra Mitch Wheat, potresti semplicemente saltare il passaggio di conversione.

[ENDEDIT]

Ecco quello che vuoi, se non fai il metodo di estensione (a meno che tu non abbia già la raccolta di potenziali uid come int, quindi usalo List<int>()invece). Questo utilizza la sintassi del metodo concatenato, che penso sia più pulita, ed esegue la conversione in int per garantire che la query possa essere utilizzata con più provider.

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

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

Grazie. Era la risposta giusta ... Ancora un pensiero? Diciamo che anche arrayuids è una query linq. In qualche modo potresti ottenere entrambe le istruzioni fino a una sola query dal database?
SpoiledTechie.com

4
Secondo MSDN, string [] implementa IEnumerable <T>, che dispone di un metodo Contains. Pertanto, non è necessario convertire la matrice in un IList <T>. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson

L'ultimo .ToString () genera errori per me. In particolare, LINQ to Entities non riconosce il metodo "System.String ToString ()" e questo metodo non può essere tradotto in un'espressione di archivio ... Dopo averlo rimosso, lambda ha funzionato per me.
Sam Stange

Lo adoro, è così facile che non lo ricordo mai.
Olaj,

@SamStange - un problema con LINQ è che ci sono così tante varianti e l'astrazione è "che perde", a volte è necessario sapere quale variante si sta utilizzando per costruire correttamente la query. Come scritto, questo funzionerebbe per LINQ to objects (e potrebbe essere LINQ to SQL). Per EF, dovresti farlo al contrario e costruire la raccolta in memoria come List<int>invece e saltare la ToStringchiamata.
tvanfosson

36

Se stai veramente cercando di replicare Contains , ma per un array, ecco un metodo di estensione e un codice di esempio per l'utilizzo:

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, dovresti inviarlo totalmente a ExtensionMethod.net Grazie per l'ottimo codice, ha risolto il mio problema oggi!
p.campbell

4
Penso che volevi dire! String.IsNullOrEmpty (str) && values.Length> 0
Greg Bogumil

Hai ragione. L'ho cambiato, anche se non ha un impatto funzionale. Uso una funzione come questa al lavoro. Dovrò controllarlo!
Jason Jackson

@ JasonJackson Mi rendo conto che questo è vecchio (ish), ma (come ha detto Greg) non vuoi un "e anche" piuttosto che un "o altro" in quel condizionale?
Tieson T.

@TiesonT. Entrambi i condizionali "oppure" e "e anche" finirebbero con lo stesso risultato restituito dalla funzione; se il !string.IsNullOrEmpty(str)controllo ha values.Length > 0avuto esito positivo, facendo sì che la condizione venga ignorata, ma la lunghezza di Values ​​era 0 , allora andrebbe al foreache quindi si interromperà immediatamente perché non ci sono voci nell'array, andando direttamente al return false.
Meowmaritus

20

Prova quanto segue.

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

2
Vorrei davvero che le persone lasciassero un commento quando ti segnano. Soprattutto perché la risposta che ho fornito è corretta al 100%.
JaredPar

Non sono stato io, ma All () non restituisce semplicemente un bool che indica dove tutti gli elementi corrispondono alla condizione? E l'inizializzazione di toSearchFor su null garantisce un'eccezione NullReferenceException.
Lucas

Ho modificato il problema null in modo che fosse ciò che intendevo digitare. Sì su tutti. Ciò garantisce efficacemente che tutte le stringhe in toSearchFor siano contenute nella stringa di input.
JaredPar

6
Non vedo affatto come questo risponda alla domanda. La domanda si è trasformata in te?
tvanfosson

15

LINQ in .NET 4.0 ha un'altra opzione per te; il metodo .Any ();

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

1
Ottima risposta, espressioni con Any()e All()metodi sono così semplici :) Posso usare t => words.All(w => t.Title.Contains(w)).
alcol fa male l'

7

O se hai già i dati in un elenco e preferisci l'altro formato 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

Che ne dite di:

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

NotSupportedException: operatori di confronto non supportati per il tipo "System.String []" Grazie ma riprovare?
SpoiledTechie.com

+1, se questo è effettivamente quello che vogliono. Non è molto chiaro dalla domanda.
Lucas

2

Questo è un esempio di un modo di scrivere un metodo di estensione (nota: non lo userei per array molto grandi; un'altra struttura dati sarebbe più appropriata ...):

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
sarebbe identico a public static bool Contains (this string [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; }
James Curran

5
string [] implementa IEnumerable <string>, quindi ha già un metodo di estensione Contains (string). Perché lo stiamo reimplementando?
Lucas

2

Questa è una risposta tardiva, ma credo che sia ancora utile .
Ho creato il pacchetto nuget NinjaNye.SearchExtension che può aiutare a risolvere proprio questo problema .:

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

Puoi anche cercare più proprietà di stringa

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

Oppure esegui un RankedSearchritorno IQueryable<IRanked<T>>che include semplicemente una proprietà che mostra quante volte sono apparsi i termini di ricerca:

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

C'è una guida più ampia nella pagina GitHub dei progetti: https://github.com/ninjanye/SearchExtensions

Spero che questo aiuti i futuri visitatori


1
Sì, è stato creato appositamente per targetizzare Entity Framework
NinjaNye

1
Posso usarlo anche con il metodo .Where ()?
Hamza Khanzada,

Sì, funziona IQueryablee IEnumerable- stai lontano dal fatto che se lo concateni da un IEnumerable, verrà eseguito in memeory piuttosto che creare una query e inviarlo alla fonte
NinjaNye

2

Metodo di estensione Linq. Funzionerà con qualsiasi oggetto IEnumerable:

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

Utilizzo:

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

bool Array2ItemsInArray1 = List1.ContainsAny(List2);

1

Credo che potresti anche fare qualcosa del genere.

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

Come "where stringArray.Contains (xx.uid.ToString ())", non è necessario avvolgerlo in una query
Lucas

0

Quindi presumo correttamente che uid sia un identificatore univoco (Guid)? Questo è solo un esempio di un possibile scenario o stai davvero cercando di trovare un guid che corrisponda a un array di stringhe?

Se questo è vero potresti voler davvero ripensare a tutto questo approccio, sembra davvero una cattiva idea. Probabilmente dovresti provare ad abbinare un Guid a un Guid

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

Onestamente non riesco a immaginare uno scenario in cui la corrispondenza di un array di stringhe utilizzando "contiene" al contenuto di un Guid sarebbe una buona idea. Per prima cosa, Contains () non garantirà l'ordine dei numeri nel Guid, quindi potresti potenzialmente abbinare più elementi. Per non parlare del confronto tra le guide in questo modo sarebbe molto più lento del semplice farlo direttamente.


0

Dovresti scriverlo al contrario, controllando che il tuo elenco di ID utente privilegiato contenga l'ID su quella riga della tabella:

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

LINQ si comporta in modo abbastanza brillante qui e lo converte in una buona istruzione 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'

che fondamentalmente incorpora il contenuto dell'array 'search' nella query sql e fa il filtraggio con la parola chiave 'IN' in SQL.


Funziona bene fintanto che non hai più di 2100 parametri.
jpierson

0

Sono riuscito a trovare una soluzione, ma non eccezionale in quanto richiede l'utilizzo di AsEnumerable () che restituirà tutti i risultati dal DB, fortunatamente ho solo 1k record nella tabella quindi non è davvero evidente, ma ecco qua .

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;

Segue il mio post originale:

Come fai il contrario? Voglio fare qualcosa di simile a quanto segue in 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;

Quello che voglio è trovare tutti gli utenti in cui il loro FullName contiene tutti gli elementi nella "ricerca". Ho provato diversi modi, ma tutti non hanno funzionato per me.

Ho anche provato

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

Questa versione trova solo quelli che contengono l'ultimo elemento nella matrice di ricerca.


0

La soluzione migliore che ho trovato è stata quella di andare avanti e creare una funzione con valori di tabella in SQL che producesse i risultati, come:

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

Quindi, trascina semplicemente la funzione nel tuo designer LINQ.dbml e chiamala come fai con gli altri oggetti. LINQ conosce anche le colonne della funzione memorizzata. Lo chiamo così:

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

Incredibilmente semplice e sfrutta davvero la potenza di SQL e LINQ nell'applicazione ... e puoi, ovviamente, generare qualsiasi funzione con valore di tabella che desideri per gli stessi effetti!


0

Credo che quello che vuoi veramente fare sia: immagina uno scenario in cui hai due database e hanno una tabella di prodotti in comune E vuoi selezionare i prodotti dalla tabella "A" che l'ID ha in comune con la "B"

usare il metodo contiene sarebbe troppo complicato per farlo, ciò che stiamo facendo è un'intersezione, e per questo esiste un metodo chiamato intersezione

un esempio da msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

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

Penso che ciò di cui hai bisogno sia facilmente risolvibile con l'intersezione


0

Controlla questo metodo di estensione:

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
from xx in table
where xx.uid.Split(',').Contains(string value )
select xx

0

Provare:

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

Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo su come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta.
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();

Veniamo a SO. Si prega di non dare risposte "solo codice". Potresti aggiungere qualche spiegazione su come questo risolve il problema e non è già coperto dalle altre 21 risposte?
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()) 
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.