Como faço para usar LINQ Contains (string []) em vez de Contains (string)

103

Eu tenho uma grande pergunta.

Recebi uma consulta linq para colocar simplesmente assim:

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

Os valores da string[]matriz seriam números como (1,45,20,10, etc ...)

o padrão para .Containsé .Contains(string).

Eu preciso fazer isso ao invés: .Contains(string[])...

EDIT: Um usuário sugeriu escrever uma classe de extensão para string[]. Eu gostaria de aprender como, mas alguém está disposto a me apontar na direção certa?

EDIT: O uid também seria um número. É por isso que é convertido em string.

Ajuda alguém?

SpoiledTechie.com
fonte
Você precisa esclarecer a aparência de um uid e o que seria considerado uma correspondência.
James Curran,
3
Um exemplo seria bom. Parece-me que a pergunta está pedindo um UID como: CA1FAB689C33 e a matriz como: {"42", "2259", "CA"}
Thomas Bratt
3
O oposto faz mais sentido: string []. Contém (xx.uid)
majkinetor

Respostas:

86

spoulson está quase certo, mas você precisa criar um List<string>desde o string[]início. Na verdade, List<int>seria melhor se uid também fosse int. List<T>apoia Contains(). Fazer uid.ToString().Contains(string[])isso implicaria que o uid como uma string contém todos os valores da matriz como uma substring ??? Mesmo que você tenha escrito o método de extensão, o sentido dele estaria errado.

[EDITAR]

A menos que você o tenha mudado e escrito string[]como Mitch Wheat demonstra, você poderá simplesmente pular a etapa de conversão.

[FINALIZAR]

Aqui está o que você deseja, se você não fizer o método de extensão (a menos que você já tenha a coleção de uids potenciais como ints - então apenas use em seu List<int>()lugar). Isso usa a sintaxe do método encadeado, que eu acho mais limpa, e faz a conversão para int para garantir que a consulta possa ser usada com mais provedores.

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

var selected = table.Where(t => uids.Contains(t.uid));
Tvanfosson
fonte
Obrigado. Foi a resposta certa ... Mais um pensamento? Vamos dizer que o arrayuids também é uma consulta linq. Há alguma maneira de reduzir as duas instruções a apenas uma consulta do banco de dados?
SpoiledTechie.com
4
De acordo com o MSDN, string [] implementa IEnumerable <T>, que tem um método Contains. Portanto, não é necessário converter a matriz em um IList <T>. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson
O último .ToString () lança erros para mim. Especificamente, LINQ to Entities não reconhece o método 'System.String ToString ()', e esse método não pode ser convertido em uma expressão de armazenamento ... Depois de removê-lo, o lambda funcionou para mim.
Sam Stange
Eu amo isso, é tão fácil que nunca me lembro.
Olaj
@SamStange - um problema com LINQ é que existem tantas variantes e a abstração é "furada", às vezes você precisa saber qual variante está usando para construir a consulta corretamente. Conforme escrito, isso funcionaria para LINQ para objetos (e pode ser LINQ para SQL). Para EF, você faria isso ao contrário e construiria a coleção na memória como em List<int>vez disso e pularia a ToStringchamada.
tvanfosson
36

Se você realmente deseja replicar Contains , mas para uma matriz, aqui está um método de extensão e um código de amostra para 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
fonte
2
+1 @Jason, você deve enviar isso totalmente para ExtensionMethod.net Obrigado pelo ótimo código, ele resolveu meu problema hoje!
p.campbell
4
Acho que você quis dizer! String.IsNullOrEmpty (str) && values.Length> 0
Greg Bogumil
Você está certo. Eu mudei, embora não tenha um impacto funcional. Eu uso uma função como esta no trabalho. Vou ter que verificar!
Jason Jackson
@JasonJackson Sei que isso é antigo (ish), mas (como Greg mencionou) você não quer um "e também" em vez de um "ou então" nessa condicional?
Tieson T.
@TiesonT. As condicionais "or else" e "and also" teriam o mesmo resultado retornado pela função; se a !string.IsNullOrEmpty(str)verificação for aprovada, fazendo com que a values.Length > 0condição seja ignorada, mas o comprimento de Values ​​for 0 , ele irá para o foreache será interrompido imediatamente porque não há entradas na matriz, indo diretamente para o return false.
Meowmaritus
20

Experimente o seguinte.

string input = "someString";
string[] toSearchFor = GetSearchStrings();
var containsAll = toSearchFor.All(x => input.Contains(x));
JaredPar
fonte
2
Eu realmente gostaria que as pessoas deixassem um comentário quando marcassem você. Principalmente porque a resposta que dei é 100% correta.
JaredPar
Não fui eu, mas All () não retorna simplesmente um bool indicando onde todos os itens correspondem à condição? E inicializando toSearchFor para nulo garante uma NullReferenceException.
Lucas
Eu editei o problema nulo para ser o que pretendia digitar. Sim em todos. Isso garante efetivamente que todas as strings em toSearchFor estejam contidas na string de entrada.
JaredPar de
6
Eu não vejo como isso responde à pergunta. A pergunta se transformou em você?
tvanfosson
15

LINQ no .NET 4.0 tem outra opção para você; o método. Qualquer ();

string[] values = new[] { "1", "2", "3" };
string data = "some string 1";
bool containsAny = values.Any(data.Contains);
RJ Lohan
fonte
1
Ótima resposta, expressões com Any()e All()métodos são tão simples :) Eu posso usar t => words.All(w => t.Title.Contains(w)).
álcool é mau
7

Ou se você já tem os dados em uma lista e prefere o outro 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
fonte
3

E se:

from xx in table
where stringarray.Contains(xx.uid.ToString())
select xx
Spoulson
fonte
NotSupportedException: Operadores de comparação não suportados para o tipo 'System.String []' Obrigado, mas tente novamente?
SpoiledTechie.com
1, se isso é realmente o que eles querem. A pergunta não está muito clara.
Lucas
2

Este é um exemplo de uma maneira de escrever um método de extensão (nota: eu não usaria isso para matrizes muito grandes; outra estrutura de dados seria mais apropriada ...):

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
fonte
1
que seria idêntico a public static bool Contains (this string [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; }
James Curran,
5
string [] implementa IEnumerable <string>, portanto, já tem um método de extensão Contains (string). Por que estamos reimplementando isso?
Lucas
2

Esta é uma resposta tardia, mas acredito que ainda seja útil .
Eu criei o pacote nuget NinjaNye.SearchExtension que pode ajudar a resolver esse problema:

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

Você também pode pesquisar várias propriedades de string

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

Ou execute um RankedSearchque retorna IQueryable<IRanked<T>>que simplesmente inclui uma propriedade que mostra quantas vezes os termos de pesquisa apareceram:

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

Há um guia mais extenso na página de projetos do GitHub: https://github.com/ninjanye/SearchExtensions

Espero que isso ajude futuros visitantes

NinjaNye
fonte
1
Sim, foi criado especificamente para o Entity Framework
NinjaNye
1
Posso usar isso com o método .Where () também?
Hamza Khanzada
Sim, funciona IQueryablee IEnumerable- lembre-se de que se você encadeá-lo de um IEnumerable, ele será executado na memória em vez de construir uma consulta e enviá-la para a fonte
NinjaNye
2

Método de extensão Linq. Funcionará com qualquer 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
fonte
1

Eu acredito que você também pode fazer algo assim.

from xx in table
where (from yy in string[] 
       select yy).Contains(xx.uid.ToString())
select xx
ctrlShiftBryan
fonte
O mesmo que "where stringArray.Contains (xx.uid.ToString ())", não há necessidade de envolvê-lo em uma consulta
Lucas
0

Portanto, estou assumindo corretamente que uid é um Identificador Único (Guid)? Este é apenas um exemplo de um cenário possível ou você está realmente tentando encontrar um guid que corresponda a um array de strings?

Se isso for verdade, você pode querer realmente repensar toda a abordagem, parece uma péssima ideia. Você provavelmente deve tentar combinar um Guid com um Guid

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

Sinceramente, não consigo imaginar um cenário em que corresponder uma matriz de string usando "contém" ao conteúdo de um Guid seria uma boa ideia. Por um lado, Contains () não garante a ordem dos números no Guid, portanto, você pode potencialmente corresponder a vários itens. Sem mencionar que comparar guias dessa forma seria muito mais lento do que apenas fazer diretamente.

justin.m.chase
fonte
0

Você deve escrever ao contrário, verificando se sua lista de IDs de usuário privilegiado contém o ID nessa linha da tabela:

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

LINQ se comporta bem aqui e o converte em uma boa instrução 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 basicamente incorpora o conteúdo do array 'search' na consulta sql e faz a filtragem com a palavra-chave 'IN' no SQL.

Gorkem Pacaci
fonte
Isso funciona bem, desde que você não tenha mais de 2100 parâmetros.
jpierson
0

Consegui encontrar uma solução, mas não muito boa, pois requer o uso de AsEnumerable () que vai retornar todos os resultados do banco de dados, felizmente só tenho 1k registros na tabela, então não é realmente perceptível, mas aqui vai .

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;

Minha postagem original é a seguinte:

Como você faz o inverso? Eu quero fazer algo como o seguinte na estrutura da entidade.

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

O que eu quero é encontrar todos os usuários onde seu FullName contenha todos os elementos em `search '. Tentei várias maneiras diferentes, mas todas não têm funcionado para mim.

Eu também tentei

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

Esta versão encontra apenas aqueles que contêm o último elemento na matriz de pesquisa.

Brett Ryan
fonte
0

A melhor solução que encontrei foi ir em frente e criar uma função com valor de tabela em SQL que produza os 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')

Em seguida, você simplesmente arrasta a função para o designer LINQ.dbml e a chama como faz com seus outros objetos. O LINQ conhece até as colunas de sua função armazenada. Eu chamo assim:

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

Incrivelmente simples e realmente utiliza o poder do SQL e do LINQ no aplicativo ... e você pode, é claro, gerar qualquer função com valor de tabela que desejar para os mesmos efeitos!

beauXjames
fonte
0

Eu acredito que o que você realmente deseja fazer é: vamos imaginar um cenário em que você tem dois bancos de dados e eles têm uma tabela de produtos em comum E você deseja selecionar produtos da tabela "A" que o id tem em comum com o "B"

usar o método contém seria muito complicado para fazer isso, o que estamos fazendo é uma interseção, e há um método chamado interseção para isso

um exemplo do msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

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

Acho que o que você precisa é facilmente resolvido com interseção

Lucas Cria
fonte
0

Verifique este método de extensão:

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

Experimentar:

var stringInput = "test";
var listOfNames = GetNames();
var result = from names in listOfNames where names.firstName.Trim().ToLower().Contains(stringInput.Trim().ToLower());
select names;
Hedego
fonte
Embora este código possa responder à pergunta, fornecer contexto adicional sobre como e / ou por que ele resolve o problema melhoraria o valor da resposta a longo prazo.
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
fonte
-1
string[] stringArray = {1,45,20,10};
from xx in table 
where stringArray.Contains(xx.uid.ToString()) 
select xx
knroc
fonte
-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
fonte