Wie verwende ich LINQ Contains (string []) anstelle von Contains (string)


103

Ich habe eine große Frage.

Ich habe eine Linq-Abfrage, um es einfach so auszudrücken:

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

Die Werte des string[]Arrays wären Zahlen wie (1,45,20,10 usw.)

Die Standardeinstellung für .Containsist .Contains(string).

Ich brauche es stattdessen, um dies zu tun: .Contains(string[])...

BEARBEITEN: Ein Benutzer schlug vor, eine Erweiterungsklasse für zu schreiben string[]. Ich würde gerne lernen wie, aber jemand, der bereit ist, mich in die richtige Richtung zu weisen?

EDIT: Die UID wäre auch eine Nummer. Deshalb wird es in einen String konvertiert.

Jemandem helfen?


Sie müssen klären, wie eine UID aussehen könnte und was als Übereinstimmung angesehen wird.
James Curran

3
Ein Beispiel wäre schön. Es klingt für mich so, als würde die Frage nach einer UID wie CA1FAB689C33 und einem Array wie {"42", "2259", "CA"} fragen
Thomas Bratt

3
Das Gegenteil macht mehr Sinn: string []. Enthält (xx.uid)
Majkinetor

Antworten:


85

Spoulson hat es fast richtig, aber Sie müssen eine List<string>von string[]zuerst erstellen . Eigentlich List<int>wäre a besser wenn uid auch ist int. List<T>unterstützt Contains(). Dies uid.ToString().Contains(string[])würde bedeuten, dass die UID als Zeichenfolge alle Werte des Arrays als Teilzeichenfolge enthält. Selbst wenn Sie die Erweiterungsmethode geschrieben hätten, wäre der Sinn falsch.

[BEARBEITEN]

Wenn Sie es nicht geändert und geschrieben haben, string[]wie Mitch Wheat demonstriert, können Sie den Konvertierungsschritt einfach überspringen.

[ENDEDIT]

Folgendes möchten Sie, wenn Sie die Erweiterungsmethode nicht ausführen (es sei denn, Sie haben bereits die Sammlung potenzieller Uids als Ints - verwenden Sie List<int>()stattdessen einfach ). Dies verwendet die verkettete Methodensyntax, die meiner Meinung nach sauberer ist, und führt die Konvertierung in int durch, um sicherzustellen, dass die Abfrage mit mehr Anbietern verwendet werden kann.

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

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

Danke dir. Es war die richtige Antwort ... Noch ein Gedanke? Nehmen wir an, die Arrayuids sind auch eine Linq-Abfrage. Wie können Sie beide Anweisungen auf nur eine Abfrage aus der Datenbank reduzieren?
SpoiledTechie.com

4
Laut MSDN implementiert string [] IEnumerable <T> mit einer Contains-Methode. Daher ist es nicht erforderlich, das Array in eine IList <T> zu konvertieren. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
Spoulson

Das letzte .ToString () wirft Fehler für mich. Insbesondere erkennt LINQ to Entities die Methode 'System.String ToString ()' nicht, und diese Methode kann nicht in einen Speicherausdruck übersetzt werden. Nach dem Entfernen hat das Lambda für mich funktioniert.
Sam Stange

Ich liebe das, es ist so einfach, dass ich mich nie daran erinnere.
Olaj

@SamStange - Ein Problem mit LINQ ist, dass es so viele Varianten gibt und die Abstraktion "undicht" ist, dass Sie manchmal wissen müssen, welche Variante Sie verwenden, um die Abfrage richtig zu erstellen. Wie geschrieben würde dies für LINQ zu Objekten funktionieren (und könnte LINQ zu SQL sein). Bei EF würden Sie es umgekehrt machen und List<int>stattdessen die Sammlung im Speicher erstellen und den ToStringAufruf überspringen .
Tvanfosson

36

Wenn Sie Contains wirklich replizieren möchten , aber ein Array suchen , finden Sie hier eine Erweiterungsmethode und einen Beispielcode zur Verwendung:

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, du solltest dies unbedingt an ExtensionMethod.net senden. Danke für den tollen Code, er hat mein Problem heute gelöst!
p.campbell

4
Ich denke du meintest! String.IsNullOrEmpty (str) && values.Length> 0
Greg Bogumil

Du hast recht. Ich habe es geändert, obwohl es keine funktionalen Auswirkungen hat. Ich benutze eine solche Funktion bei der Arbeit. Ich muss es überprüfen!
Jason Jackson

@JasonJackson Mir ist klar, dass dies alt ist (ish), aber (wie Greg erwähnte) willst du nicht ein "und auch" anstatt ein "oder sonst" in dieser Bedingung?
Tieson T.

@ TiesonT. Sowohl die Bedingungen "oder sonst" als auch "und auch" würden das gleiche Ergebnis liefern, das von der Funktion zurückgegeben wird. Wenn die !string.IsNullOrEmpty(str)Prüfung bestanden wurde und die values.Length > 0Bedingung ignoriert wurde , die Länge der Werte jedoch 0 war , wurde sie an die gesendet foreachund sofort unterbrochen, da das Array keine Einträge enthält und direkt an die return false.
Meowmaritus

20

Versuche Folgendes.

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

2
Ich wünschte wirklich, die Leute würden einen Kommentar hinterlassen, wenn sie dich notieren. Zumal die Antwort, die ich gegeben habe, zu 100% richtig ist.
JaredPar

Ich war es nicht, aber gibt All () nicht einfach einen Bool zurück, der angibt, wo alle Elemente der Bedingung entsprechen? Durch das Initialisieren von toSearchFor auf Null wird eine NullReferenceException garantiert.
Lucas

Ich habe das Null-Problem so bearbeitet, dass es das ist, was ich eingeben wollte. Ja zu allen. Dadurch wird effektiv sichergestellt, dass alle Zeichenfolgen in toSearchFor in der Eingabezeichenfolge enthalten sind.
JaredPar

6
Ich sehe nicht, wie dies die Frage überhaupt beantwortet. Hat sich die Frage auf dich verwandelt?
Tvanfosson

15

LINQ in .NET 4.0 bietet eine weitere Option für Sie. die .Any () -Methode;

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

1
Tolle Antwort, Ausdrücke mit Any()und All()Methoden sind so einfach :) Ich kann verwenden t => words.All(w => t.Title.Contains(w)).
Alkohol ist böse

7

Oder wenn Sie die Daten bereits in einer Liste haben und das andere Linq-Format bevorzugen :)

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

Wie wäre es mit:

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

NotSupportedException: Vergleichsoperatoren werden für Typ 'System.String []' nicht unterstützt. Danke, aber versuchen Sie es erneut?
SpoiledTechie.com

+1, wenn dies tatsächlich das ist, was sie wollen. Es ist nicht sehr klar aus der Frage.
Lucas

2

Dies ist ein Beispiel für eine Möglichkeit, eine Erweiterungsmethode zu schreiben (Hinweis: Ich würde dies nicht für sehr große Arrays verwenden; eine andere Datenstruktur wäre besser geeignet ...):

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
das wäre identisch mit public static bool Contains (dieser String [] Stringarray, String Pat) {return Array.IndexOf (Stringarray, Pat)! = -1; }
James Curran

5
string [] implementiert IEnumerable <string>, sodass es bereits eine Contains (string) -Erweiterungsmethode gibt. Warum setzen wir das um?
Lucas

2

Dies ist eine späte Antwort, aber ich glaube, dass sie immer noch nützlich ist .
Ich habe das NinjaNye.SearchExtension- Nuget-Paket erstellt, das zur Lösung dieses Problems beitragen kann:

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

Sie können auch mehrere Zeichenfolgeneigenschaften durchsuchen

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

Oder führen Sie eine RankedSearchRückgabe durch, IQueryable<IRanked<T>>die einfach eine Eigenschaft enthält, die angibt, wie oft die Suchbegriffe angezeigt wurden:

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

Auf der GitHub-Seite des Projekts finden Sie eine ausführlichere Anleitung: https://github.com/ninjanye/SearchExtensions

Hoffe das hilft zukünftigen Besuchern


1
Ja, es wurde speziell für Entity Framework entwickelt
NinjaNye

1
Kann ich dies auch mit der Methode .Where () verwenden?
Hamza Khanzada

Ja, es funktioniert auf IQueryableund IEnumerable- seien Sie einfach weg, wenn Sie es an eine IEnumerable ketten, wird es im Speicher ausgeführt, anstatt eine Abfrage zu erstellen und an die Quelle zu
senden

2

Linq-Erweiterungsmethode. Funktioniert mit jedem IEnumerable-Objekt:

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

Verwendung:

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

bool Array2ItemsInArray1 = List1.ContainsAny(List2);

1

Ich glaube, Sie könnten auch so etwas tun.

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

Wie "where stringArray.Contains (xx.uid.ToString ())" muss es nicht in eine Abfrage eingeschlossen werden
Lucas,

0

Nehme ich also richtig an, dass uid eine eindeutige Kennung (Guid) ist? Ist dies nur ein Beispiel für ein mögliches Szenario oder versuchen Sie wirklich, eine Anleitung zu finden, die einer Reihe von Zeichenfolgen entspricht?

Wenn dies zutrifft, möchten Sie vielleicht diesen ganzen Ansatz wirklich überdenken, scheint dies eine wirklich schlechte Idee zu sein. Sie sollten wahrscheinlich versuchen, eine Guid einer Guid zuzuordnen

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

Ich kann mir ehrlich gesagt kein Szenario vorstellen, in dem es eine gute Idee wäre, ein String-Array mit "enthält" an den Inhalt eines Guid anzupassen. Zum einen garantiert Contains () nicht die Reihenfolge der Nummern in den Guid, sodass Sie möglicherweise mit mehreren Elementen übereinstimmen können. Ganz zu schweigen davon, dass das Vergleichen von Guids auf diese Weise viel langsamer wäre, als es direkt zu tun.


0

Sie sollten es umgekehrt schreiben und überprüfen, ob Ihre privilegierte Benutzer-ID-Liste die ID in dieser Tabellenzeile enthält:

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

LINQ verhält sich hier recht hell und konvertiert es in eine gute SQL-Anweisung:

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'

Hiermit wird der Inhalt des Arrays 'search' grundsätzlich in die SQL-Abfrage eingebettet und in SQL mit dem Schlüsselwort 'IN' gefiltert.


Dies funktioniert einwandfrei, solange Sie nicht über 2100 Parameter haben.
Jpierson

0

Ich habe es geschafft, eine Lösung zu finden, aber keine großartige, da AsEnumerable () verwendet werden muss, das alle Ergebnisse aus der Datenbank zurückgibt. Zum Glück habe ich nur 1k Datensätze in der Tabelle, so dass es nicht wirklich auffällt, aber hier geht es weiter .

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;

Mein ursprünglicher Beitrag folgt:

Wie machst du das Gegenteil? Ich möchte im Entity Framework Folgendes tun.

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

Ich möchte alle Benutzer finden, deren vollständiger Name alle Elemente in "Suche" enthält. Ich habe verschiedene Methoden ausprobiert, die alle nicht für mich funktioniert haben.

Ich habe es auch versucht

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

Diese Version findet nur diejenigen, die das letzte Element im Sucharray enthalten.


0

Die beste Lösung, die ich gefunden habe, bestand darin, eine Tabellenwertfunktion in SQL zu erstellen, die die Ergebnisse liefert, z.

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

Anschließend ziehen Sie die Funktion einfach in Ihren LINQ.dbml-Designer und rufen sie wie Ihre anderen Objekte auf. Der LINQ kennt sogar die Spalten Ihrer gespeicherten Funktion. Ich nenne es so ::

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

Unglaublich einfach und nutzt die Leistung von SQL und LINQ in der Anwendung wirklich aus ... und Sie können natürlich jede gewünschte Tabellenwertfunktion für die gleichen Effekte generieren!


0

Ich glaube, was Sie wirklich tun möchten, ist: Stellen wir uns ein Szenario vor, in dem Sie zwei Datenbanken haben und diese eine gemeinsame Produkttabelle haben. Und Sie möchten Produkte aus der Tabelle "A" auswählen, die die ID mit dem "B" gemeinsam hat.

Die Verwendung der enthaltenen Methoden wäre zu kompliziert, um dies zu tun. Wir tun eine Kreuzung, und dafür gibt es eine Methode namens Kreuzung

Ein Beispiel aus msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

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

Ich denke, was Sie brauchen, lässt sich leicht mit der Kreuzung lösen


0

Überprüfen Sie diese Erweiterungsmethode:

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

Versuchen:

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

Während dieser Code die Frage möglicherweise beantwortet, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und / oder warum das Problem gelöst wird, den langfristigen Wert der Antwort verbessern.
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;

-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()) 
  }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.