Comment utiliser LINQ Contains (string []) au lieu de Contains (string)


103

J'ai une grande question.

J'ai une requête linq pour dire que cela ressemble simplement à ceci:

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

Les valeurs du string[]tableau seraient des nombres comme (1,45,20,10, etc ...)

la valeur par défaut pour .Containsest .Contains(string).

J'en ai besoin pour faire ceci à la place: .Contains(string[])...

EDIT: Un utilisateur a suggéré d'écrire une classe d'extension pour string[]. J'aimerais savoir comment, mais quelqu'un veut-il me diriger dans la bonne direction?

EDIT: L'uid serait également un nombre. C'est pourquoi il est converti en chaîne.

Aider quelqu'un?


Vous devez préciser à quoi pourrait ressembler un uid et ce qui serait considéré comme une correspondance.
James Curran

3
Un exemple serait bien. Il me semble que la question demande un UID comme: CA1FAB689C33 et le tableau comme: {"42", "2259", "CA"}
Thomas Bratt

3
L'opposé a plus de sens: string []. Contient (xx.uid)
majkinetor

Réponses:


87

spoulson l' a presque raison, mais vous devez créer un à List<string>partir de la string[]première. En fait, ce List<int>serait mieux si uid l'est également int. List<T>prend en charge Contains(). Cela uid.ToString().Contains(string[])impliquerait que l'uid en tant que chaîne contienne toutes les valeurs du tableau en tant que sous-chaîne ??? Même si vous aviez écrit la méthode d'extension, le sens serait faux.

[ÉDITER]

À moins que vous ne l'ayez changé et que vous ne l'ayez écrit string[]comme le montre Mitch Wheat, vous pourrez simplement ignorer l'étape de conversion.

[ENDEDIT]

Voici ce que vous voulez, si vous ne faites pas la méthode d'extension (sauf si vous avez déjà la collection d'uids potentiels comme ints - utilisez simplement à la List<int>()place). Cela utilise la syntaxe de la méthode chaînée, que je pense plus propre, et effectue la conversion en int pour garantir que la requête peut être utilisée avec plus de fournisseurs.

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

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

Je vous remercie. C'était la bonne réponse ... Encore une réflexion? Disons que les arrayuids sont également une requête linq. De toute façon, vous pourriez obtenir les deux instructions en une seule requête de la base de données?
SpoiledTechie.com

4
Selon MSDN, string [] implémente IEnumerable <T>, qui a une méthode Contains. Par conséquent, il n'est pas nécessaire de convertir le tableau en IList <T>. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson

Le dernier .ToString () jette des erreurs pour moi. Plus précisément, LINQ to Entities ne reconnaît pas la méthode «System.String ToString ()», et cette méthode ne peut pas être traduite en une expression de magasin .... Après l'avoir supprimée, le lambda a fonctionné pour moi.
Sam Stange

J'adore ça, c'est tellement facile que je ne m'en souviens jamais.
Olaj le

@SamStange - un problème avec LINQ est qu'il y a tellement de variantes et que l'abstraction est "fuyante", vous avez parfois besoin de savoir quelle variante vous utilisez pour construire correctement la requête. Tel qu'il est écrit, cela fonctionnerait pour LINQ to objets (et peut être LINQ to SQL). Pour EF, vous le feriez dans l'autre sens et construiriez la collection en mémoire à la List<int>place et ignorer l' ToStringappel.
tvanfosson

36

Si vous cherchez vraiment à répliquer Contains , mais pour un tableau, voici une méthode d'extension et un exemple de code à utiliser:

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, vous devriez totalement soumettre ceci à ExtensionMethod.net Merci pour le bon code, il a résolu mon problème aujourd'hui!
p.campbell

4
Je pense que vous vouliez dire! String.IsNullOrEmpty (str) && values.Length> 0
Greg Bogumil

Vous avez raison. Je l'ai changé, même si cela n'a pas d'impact fonctionnel. J'utilise une fonction comme celle-ci au travail. Je vais devoir le vérifier!
Jason Jackson

@JasonJackson Je me rends compte que c'est vieux (ish), mais (comme Greg l'a mentionné) ne voulez-vous pas un "et aussi" plutôt qu'un "ou autre" dans ce conditionnel?
Tieson T.

@TiesonT. Les conditionnelles "ou else" et "et aussi" aboutiraient au même résultat renvoyé par la fonction; si la !string.IsNullOrEmpty(str)vérification réussissait, la values.Length > 0condition étant ignorée, mais que la longueur de Values ​​était égale à 0 , alors elle irait au foreach, puis se briserait immédiatement car il n'y a pas d'entrées dans le tableau, allant directement au return false.
Meowmaritus

20

Essayez ce qui suit.

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

2
J'aimerais vraiment que les gens laissent un commentaire lorsqu'ils vous notent. D'autant que la réponse que j'ai fournie est correcte à 100%.
JaredPar

Ce n'était pas moi, mais All () ne renvoie-t-il pas simplement un booléen indiquant où tous les éléments correspondent à la condition? Et l'initialisation de toSearchFor à null garantit une NullReferenceException.
Lucas

J'ai modifié le problème nul pour qu'il corresponde à ce que je voulais taper. Oui sur tout. Cela garantit efficacement que toutes les chaînes de toSearchFor sont contenues dans la chaîne d'entrée.
JaredPar

6
Je ne vois pas du tout comment cela répond à la question. La question a-t-elle évolué sur vous?
tvanfosson le

15

LINQ dans .NET 4.0 a une autre option pour vous; la méthode .Any ();

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

1
Excellente réponse, les expressions avec Any()et les All()méthodes sont si simples :) Je peux utiliser t => words.All(w => t.Title.Contains(w)).
alcool est mauvais

7

Ou si vous avez déjà les données dans une liste et préférez l'autre 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

Que diriez-vous:

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

NotSupportedException: Opérateurs de comparaison non pris en charge pour le type 'System.String []' Merci mais réessayez?
SpoiledTechie.com

+1, si c'est réellement ce qu'ils veulent. Ce n'est pas très clair d'après la question.
Lucas

2

Ceci est un exemple d'une manière d'écrire une méthode d'extension (note: je ne l'utiliserais pas pour de très grands tableaux; une autre structure de données serait plus appropriée ...):

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

5
string [] implémente IEnumerable <string>, il a donc déjà une méthode d'extension Contains (string). Pourquoi réinstaurons-nous cela?
Lucas

2

C'est une réponse tardive, mais je pense qu'elle est toujours utile .
J'ai créé le package nuget NinjaNye.SearchExtension qui peut aider à résoudre ce problème:

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

Vous pouvez également rechercher plusieurs propriétés de chaîne

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

Ou effectuez un RankedSearchqui renvoie IQueryable<IRanked<T>>qui inclut simplement une propriété qui montre combien de fois les termes de recherche sont apparus:

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

Il existe un guide plus complet sur la page GitHub des projets: https://github.com/ninjanye/SearchExtensions

J'espère que cela aidera les futurs visiteurs


1
Oui, il a été spécialement conçu pour cibler Entity Framework
NinjaNye

1
Puis-je également l'utiliser avec la méthode .Where ()?
Hamza Khanzada

Oui, cela fonctionne IQueryableet IEnumerable- soyez juste loin que si vous l'enchaînez à partir d'un IEnumerable, il fonctionnera dans la mémoire plutôt que de créer une requête et de l'envoyer à la source
NinjaNye

2

Méthode d'extension Linq. Fonctionnera avec n'importe quel objet IEnumerable:

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

Usage:

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

bool Array2ItemsInArray1 = List1.ContainsAny(List2);

1

Je pense que vous pourriez aussi faire quelque chose comme ça.

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

Identique à "where stringArray.Contains (xx.uid.ToString ())", pas besoin de l'enrouler dans une requête
Lucas

0

Alors est-ce que je suppose correctement que uid est un identifiant unique (Guid)? Est-ce juste un exemple d'un scénario possible ou essayez-vous vraiment de trouver un guid qui correspond à un tableau de chaînes?

Si cela est vrai, vous voudrez peut-être vraiment repenser toute cette approche, cela semble être une très mauvaise idée. Vous devriez probablement essayer de faire correspondre un Guid à un Guid

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

Honnêtement, je ne peux pas imaginer un scénario où faire correspondre un tableau de chaînes en utilisant "contient" au contenu d'un Guid serait une bonne idée. D'une part, Contains () ne garantira pas l'ordre des nombres dans le Guid afin que vous puissiez potentiellement faire correspondre plusieurs éléments. Sans parler de comparer les guides de cette façon serait bien plus lent que de le faire directement.


0

Vous devriez l'écrire dans l'autre sens, en vérifiant que votre liste d'identifiants d'utilisateurs privilégiés contient l'identifiant sur cette ligne de la table:

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

LINQ se comporte assez brillamment ici et le convertit en une bonne instruction 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'

qui intègre fondamentalement le contenu du tableau 'search' dans la requête SQL, et effectue le filtrage avec le mot-clé 'IN' dans SQL.


Cela fonctionne bien tant que vous n'avez pas plus de 2100 paramètres.
jpierson

0

J'ai réussi à trouver une solution, mais pas excellente car elle nécessite l'utilisation de AsEnumerable () qui va renvoyer tous les résultats de la base de données, heureusement je n'ai que 1k enregistrements dans le tableau, donc ce n'est pas vraiment perceptible, mais voici .

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;

Mon message d'origine suit:

Comment faites-vous l'inverse? Je veux faire quelque chose comme ce qui suit dans le cadre d'entité.

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

Ce que je veux, c'est trouver tous les utilisateurs où leur nom complet contient tous les éléments de la recherche. J'ai essayé un certain nombre de façons différentes, qui n'ont pas toutes fonctionné pour moi.

J'ai aussi essayé

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

Cette version ne trouve que ceux qui contiennent le dernier élément du tableau de recherche.


0

La meilleure solution que j'ai trouvée était d'aller de l'avant et de créer une fonction table en SQL qui produit les résultats, tels que:

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

Ensuite, vous faites simplement glisser la fonction dans votre concepteur LINQ.dbml et l'appelez comme vous le faites pour vos autres objets. Le LINQ connaît même les colonnes de votre fonction stockée. Je l'appelle comme ceci:

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

Incroyablement simple et utilise vraiment la puissance de SQL et LINQ dans l'application ... et vous pouvez, bien sûr, générer n'importe quelle fonction de table que vous voulez pour les mêmes effets!


0

Je crois que ce que vous voulez vraiment faire est: imaginons un scénario vous avez deux bases de données et ils ont une table de produits en commun Et vous voulez sélectionner des produits de la table "A" que l'id a en commun avec le "B"

utiliser la méthode contient serait trop compliqué pour faire cela ce que nous faisons est une intersection, et il existe une méthode appelée intersection pour cela

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

int [] nombres = (0, 2, 4, 5, 6, 8, 9); int [] nombresB = (1, 3, 5, 7, 8); var = commonNumbers nombresA.Intersect (nombresB);

Je pense que ce dont vous avez besoin est facilement résolu avec l'intersection


0

Vérifiez cette méthode d'extension:

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

Essayer:

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

Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la façon et / ou pourquoi il résout le problème améliorerait la valeur à long terme de la réponse.
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();

Nous, arrivons à SO. Veuillez ne pas donner de réponses «code seulement». Pourriez-vous ajouter des explications sur la façon dont cela résout le problème et n'est pas déjà couvert par les 21 autres réponses?
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()) 
  }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.