¿Cómo uso LINQ Contiene (cadena []) en lugar de Contiene (cadena)?

103

Tengo una gran pregunta.

Tengo una consulta de linq para ponerlo simplemente se ve así:

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

Los valores de la string[]matriz serían números como (1,45,20,10, etc ...)

el valor predeterminado para .Containses .Contains(string).

Lo necesito para hacer esto en su lugar: .Contains(string[])...

EDITAR: Un usuario sugirió escribir una clase de extensión para string[]. Me gustaría aprender cómo, pero ¿alguien está dispuesto a señalarme en la dirección correcta?

EDITAR: El uid también sería un número. Por eso se convierte en una cadena.

Ayudar a alguien?

SpoiledTechie.com
fuente
Debe aclarar cómo se vería un uid y qué se consideraría una coincidencia.
James Curran
3
Un ejemplo estaría bien. Me parece que la pregunta es pedir un UID como: CA1FAB689C33 y la matriz como: {"42", "2259", "CA"}
Thomas Bratt
3
Lo opuesto tiene más sentido: cadena []. Contiene (xx.uid)
majkinetor

Respuestas:

86

spoulson lo tiene casi bien, pero necesitas crear un List<string>desde el string[]principio. En realidad List<int>, sería mejor si uid también int. List<T>apoyos Contains(). Hacerlo uid.ToString().Contains(string[])implicaría que el uid como una cadena contiene todos los valores de la matriz como una subcadena ??? Incluso si escribiera el método de extensión, el sentido sería incorrecto.

[EDITAR]

A menos que lo cambie y lo escriba string[]como lo demuestra Mitch Wheat, entonces podrá omitir el paso de conversión.

[ENDEDIT]

Esto es lo que desea, si no usa el método de extensión (a menos que ya tenga la colección de posibles uids como ints, entonces use List<int>()en su lugar). Esto usa la sintaxis del método encadenado, que creo que es más limpia, y realiza la conversión a int para garantizar que la consulta se pueda usar con más proveedores.

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

var selected = table.Where(t => uids.Contains(t.uid));
tvanfosson
fuente
Gracias. Fue la respuesta correcta ... ¿Un pensamiento más? Digamos que arrayuids también es una consulta de linq. ¿De alguna manera podría reducir ambas declaraciones a una sola consulta de la base de datos?
SpoiledTechie.com
4
Según MSDN, string [] implementa IEnumerable <T>, que tiene un método Contains. Por lo tanto, no es necesario convertir la matriz a IList <T>. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson
El último .ToString () arroja errores para mí. Específicamente, LINQ to Entities no reconoce el método 'System.String ToString ()', y este método no se puede traducir a una expresión de tienda ... Después de eliminarlo, el lambda funcionó para mí.
Sam Stange
Me encanta esto, es tan fácil que nunca lo recuerdo.
Olaj
@SamStange: un problema con LINQ es que hay tantas variantes y la abstracción tiene "fugas", que a veces necesita saber qué variante está utilizando para construir la consulta correctamente. Como está escrito, esto funcionaría para objetos LINQ to (y puede ser LINQ to SQL). Para EF, lo haría al revés y construiría la colección en la memoria como en su List<int>lugar y omitiría la ToStringllamada.
tvanfosson
36

Si realmente está buscando replicar Contains , pero para una matriz, aquí hay un método de extensión y un código de muestra para su uso:

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;
        }
    }
}
Jason Jackson
fuente
2
+1 @Jason, deberías enviar esto totalmente a ExtensionMethod.net ¡ Gracias por el gran código, resolvió mi problema hoy!
p.campbell
4
Creo que quisiste decir! String.IsNullOrEmpty (str) && values.Length> 0
Greg Bogumil
Tienes razón. Lo cambié, aunque no tiene un impacto funcional. Utilizo una función como esta en el trabajo. ¡Tendré que comprobarlo!
Jason Jackson
@JasonJackson Me doy cuenta de que esto es antiguo (ish), pero (como mencionó Greg) ¿no quieres un "y también" en lugar de un "o más" en ese condicional?
Tieson T.
@TiesonT. Tanto los condicionales "o si no" y "y también" terminarían con el mismo resultado devuelto por la función; si la !string.IsNullOrEmpty(str)verificación pasa, lo values.Length > 0que hace que se ignore la condición, pero la longitud de los valores era 0 , entonces iría al foreachy luego se rompería inmediatamente porque no hay entradas en el arreglo, yendo directamente al return false.
Meowmaritus
20

Intente lo siguiente.

string input = "someString";
string[] toSearchFor = GetSearchStrings();
var containsAll = toSearchFor.All(x => input.Contains(x));
JaredPar
fuente
2
Realmente desearía que la gente dejara un comentario cuando te marquen. Especialmente porque la respuesta que proporcioné es 100% correcta.
JaredPar
No fui yo, pero ¿no devuelve All () simplemente un bool que indica dónde todos los elementos coinciden con la condición? E inicializar toSearchFor para nulo garantiza una NullReferenceException.
Lucas
Edité el problema nulo para que fuera lo que pretendía escribir. Sí a todos. Esto asegura efectivamente que todas las cadenas de toSearchFor estén contenidas dentro de la cadena de entrada.
JaredPar
6
No veo cómo esto responde a la pregunta en absoluto. ¿La pregunta se transformó en ti?
tvanfosson
15

LINQ en .NET 4.0 tiene otra opción para ti; el método .Any ();

string[] values = new[] { "1", "2", "3" };
string data = "some string 1";
bool containsAny = values.Any(data.Contains);
RJ Lohan
fuente
1
Excelente respuesta, las expresiones con Any()y los All()métodos son tan simples :) Puedo usar t => words.All(w => t.Title.Contains(w)).
alcohol es malvado
7

O si ya tiene los datos en una lista y prefiere el otro 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();
JumpingJezza
fuente
3

Qué tal si:

from xx in table
where stringarray.Contains(xx.uid.ToString())
select xx
Spoulson
fuente
NotSupportedException: los operadores de comparación no son compatibles con el tipo 'System.String []' Gracias, pero ¿intentarlo de nuevo?
SpoiledTechie.com
+1, si esto es realmente lo que quieren. No queda muy claro a partir de la pregunta.
Lucas
2

Este es un ejemplo de una forma de escribir un método de extensión (nota: no usaría esto para matrices muy grandes; otra estructura de datos sería más apropiada ...):

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;
        }
    }
}
Trigo Mitch
fuente
1
eso sería idéntico a public static bool Contiene (esta cadena [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; }
James Curran
5
string [] implementa IEnumerable <string>, por lo que ya tiene un método de extensión Contains (string). ¿Por qué estamos reimplementando esto?
Lucas
2

Esta es una respuesta tardía, pero creo que sigue siendo útil .
He creado el paquete nuget NinjaNye.SearchExtension que puede ayudar a resolver este mismo problema:

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

También puede buscar varias propiedades de cadena

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

O realice un RankedSearchwhich devuelve IQueryable<IRanked<T>>que simplemente incluye una propiedad que muestra cuántas veces aparecieron los términos de búsqueda:

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

Hay una guía más extensa en la página de proyectos de GitHub: https://github.com/ninjanye/SearchExtensions

Espero que esto ayude a los futuros visitantes

NinjaNye
fuente
1
Sí, se ha creado específicamente para apuntar a Entity Framework
NinjaNye
1
¿Puedo usar esto con el método .Where () también?
Hamza Khanzada
Sí, funciona IQueryabley IEnumerable, solo tenga cuidado, ya que si lo encadena un IEnumerable, se ejecutará en memoria en lugar de crear una consulta y enviarla a la fuente
NinjaNye
2

Método de extensión Linq. Funcionará con cualquier objeto IEnumerable:

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

Uso:

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

bool Array2ItemsInArray1 = List1.ContainsAny(List2);
kravits88
fuente
1

Creo que también podrías hacer algo como esto.

from xx in table
where (from yy in string[] 
       select yy).Contains(xx.uid.ToString())
select xx
ctrlShiftBryan
fuente
Igual que "where stringArray.Contains (xx.uid.ToString ())", no es necesario envolverlo en una consulta
Lucas
0

Entonces, ¿estoy asumiendo correctamente que uid es un identificador único (Guid)? ¿Es esto solo un ejemplo de un escenario posible o realmente está tratando de encontrar una guía que coincida con una matriz de cadenas?

Si esto es cierto, es posible que desee reconsiderar todo este enfoque, parece una muy mala idea. Probablemente debería intentar hacer coincidir un Guid con un Guid

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

Honestamente, no puedo imaginar un escenario en el que hacer coincidir una matriz de cadenas utilizando "contiene" con el contenido de un Guid sería una buena idea. Por un lado, Contains () no garantizará el orden de los números en el Guid, por lo que podría hacer coincidir varios elementos. Sin mencionar que comparar las guías de esta manera sería mucho más lento que hacerlo directamente.

Justin M. Chase
fuente
0

Debe escribirlo al revés, verificando que su lista de identificación de usuario privilegiado contenga la identificación en esa fila de la tabla:

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

LINQ se comporta bastante brillante aquí y lo convierte en una buena declaración 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'

que básicamente incrusta el contenido de la matriz 'buscar' en la consulta sql, y realiza el filtrado con la palabra clave 'IN' en SQL.

Gorkem Pacaci
fuente
Esto funciona bien siempre que no tenga más de 2100 parámetros.
jpierson
0

Me las arreglé para encontrar una solución, pero no excelente, ya que requiere el uso de AsEnumerable () que devolverá todos los resultados de la base de datos, afortunadamente solo tengo 1k registros en la tabla, por lo que no es realmente notable, pero aquí va .

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;

Mi publicación original es la siguiente:

¿Cómo se hace al revés? Quiero hacer algo como lo siguiente en el marco de la entidad.

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

Lo que quiero es encontrar todos los usuarios donde su FullName contiene todos los elementos en "búsqueda". Probé varias formas diferentes, y todas no me han funcionado.

También lo he intentado

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

Esta versión solo encuentra aquellos que contienen el último elemento en la matriz de búsqueda.

Brett Ryan
fuente
0

La mejor solución que encontré fue seguir adelante y crear una función con valores de tabla en SQL que produzca los resultados, como:

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

Luego, simplemente arrastre la función a su diseñador LINQ.dbml y llámelo como lo hace con sus otros objetos. El LINQ incluso conoce las columnas de su función almacenada. Lo llamo así:

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

Increíblemente simple y realmente utiliza el poder de SQL y LINQ en la aplicación ... y, por supuesto, puede generar cualquier función con valor de tabla que desee para los mismos efectos.

beauXjames
fuente
0

Creo que lo que realmente quieres hacer es: imaginemos un escenario en el que tienes dos bases de datos y tienen una tabla de productos en común y quieres seleccionar productos de la tabla "A" que id tiene en común con la "B".

usar el método contiene sería demasiado complicado para hacer esto, lo que estamos haciendo es una intersección, y hay un método llamado intersección para eso

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

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

Creo que lo que necesitas se resuelve fácilmente con intersección.

Lucas Cria
fuente
0

Compruebe este método de extensión:

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;
        }
    }
}
ron.camaron
fuente
0
from xx in table
where xx.uid.Split(',').Contains(string value )
select xx
usuario1119399
fuente
0

Tratar:

var stringInput = "test";
var listOfNames = GetNames();
var result = from names in listOfNames where names.firstName.Trim().ToLower().Contains(stringInput.Trim().ToLower());
select names;
Hedego
fuente
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre cómo y / o por qué resuelve el problema mejoraría el valor de la respuesta a largo plazo.
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;
Hari Lakkakula
fuente
-1
string[] stringArray = {1,45,20,10};
from xx in table 
where stringArray.Contains(xx.uid.ToString()) 
select xx
Knroc
fuente
-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()) 
  }
EVONZ
fuente