Làm cách nào để sử dụng LINQ Chứa (chuỗi []) thay vì Chứa (chuỗi)


103

Tôi có một câu hỏi lớn.

Tôi nhận được một truy vấn linq để đặt nó đơn giản như sau:

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

Các giá trị của string[]mảng sẽ là các số như (1,45,20,10, v.v.)

mặc định cho .Contains.Contains(string).

Tôi cần nó để làm điều này thay thế: .Contains(string[])...

CHỈNH SỬA: Một người dùng đã đề xuất viết một lớp mở rộng cho string[]. Tôi muốn học cách làm thế nào, nhưng có ai sẵn sàng chỉ tôi đi đúng hướng không?

CHỈNH SỬA: Uid cũng sẽ là một số. Đó là lý do tại sao nó được chuyển đổi thành một chuỗi.

Giúp ai?


Bạn cần phải làm rõ một uid có thể trông như thế nào, và những gì sẽ được coi là một trận đấu.
James Curran

3
Một ví dụ sẽ rất hay. Tôi nghe có vẻ như câu hỏi đang yêu cầu một UID như: CA1FAB689C33 và mảng như: {"42", "2259", "CA"}
Thomas Bratt 12/10/08

3
Ngược lại có ý nghĩa hơn: string [] Chứa (xx.uid).
majkinetor

Câu trả lời:


86

spoulson có nó gần đúng, nhưng bạn cần tạo một List<string>từ string[]đầu tiên. Trên thực tế a List<int>sẽ tốt hơn nếu uid cũng vậy int. List<T>hỗ trợ Contains(). Việc làm uid.ToString().Contains(string[])sẽ ngụ ý rằng uid dưới dạng một chuỗi chứa tất cả các giá trị của mảng dưới dạng một chuỗi con ??? Ngay cả khi bạn đã viết phương thức mở rộng, cảm giác của nó sẽ sai.

[BIÊN TẬP]

Trừ khi bạn đã thay đổi nó xung quanh và viết nó string[]như Mitch Wheat thể hiện, khi đó bạn chỉ có thể bỏ qua bước chuyển đổi.

[ENDEDIT]

Đây là những gì bạn muốn, nếu bạn không thực hiện phương pháp mở rộng (trừ khi bạn đã có tập hợp các uid tiềm năng dưới dạng int - thì chỉ cần sử dụng List<int>()thay thế). Điều này sử dụng cú pháp phương thức chuỗi, mà tôi nghĩ là rõ ràng hơn và thực hiện chuyển đổi thành int để đảm bảo rằng truy vấn có thể được sử dụng với nhiều nhà cung cấp hơn.

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

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

Cảm ơn bạn. Đó là câu trả lời đúng ... Một suy nghĩ nữa? Hãy nói rằng các arrayuids cũng là một truy vấn linq. Bất kỳ cách nào bạn có thể lấy cả hai câu lệnh xuống chỉ một truy vấn từ cơ sở dữ liệu?
SpoiledTechie.com

4
Theo MSDN, chuỗi [] triển khai IEnumerable <T>, có phương thức Chứa. Do đó, không cần thiết phải chuyển đổi mảng thành IList <T>. msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson

.ToString () cuối cùng ném lỗi cho tôi. Cụ thể, LINQ to Entities không nhận ra phương thức 'System.String ToString ()' và phương thức này không thể được dịch thành một biểu thức lưu trữ .... Sau khi loại bỏ nó, lambda đã làm việc cho tôi.
Sam Stange

Tôi thích điều này, nó dễ dàng đến mức tôi không bao giờ nhớ được.
Olaj

@SamStange - một vấn đề với LINQ là có quá nhiều biến thể và phần trừu tượng bị "rò rỉ", đôi khi bạn cần biết mình đang sử dụng biến thể nào để xây dựng truy vấn đúng cách. Như đã viết, điều này sẽ hoạt động đối với LINQ tới các đối tượng (và có thể là LINQ to SQL). Đối với EF, bạn sẽ làm theo cách khác và List<int>thay vào đó, xây dựng bộ sưu tập trong bộ nhớ và bỏ qua ToStringcuộc gọi.
tvanfosson

36

Nếu bạn thực sự muốn sao chép Chứa , nhưng đối với một mảng, đây là một phương thức mở rộng và mã mẫu để sử dụng:

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, bạn hoàn toàn nên gửi cái này đến ExtensionMethod.net Cảm ơn vì mã tuyệt vời, nó đã giải quyết được vấn đề của tôi hôm nay!
p.campbell

4
Tôi nghĩ rằng bạn có nghĩa là string.IsNullOrEmpty (str) && values.Length> 0!
Greg Bogumił

Bạn đúng rồi. Tôi đã thay đổi nó, mặc dù nó không có tác động chức năng. Tôi sử dụng một chức năng như thế này tại nơi làm việc. Tôi sẽ phải kiểm tra nó!
Jason Jackson

@JasonJackson Tôi nhận ra điều này đã cũ (ish), nhưng (như Greg đã đề cập) bạn không muốn một "và cũng" chứ không phải "hoặc khác" trong điều kiện đó?
Tieson T.

@TiesonT. Cả hai điều kiện "hoặc else" và "và cũng" sẽ kết thúc với cùng một kết quả được trả về bởi hàm; nếu !string.IsNullOrEmpty(str)kiểm tra được thông qua, khiến values.Length > 0điều kiện bị bỏ qua, nhưng độ dài của Giá trị bằng 0 , thì nó sẽ chuyển đến foreachvà sau đó ngắt ngay lập tức vì không có mục nhập nào trong mảng, sẽ chuyển trực tiếp đến return false.
Meowmaritus

20

Hãy thử những điều sau đây.

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

2
Tôi thực sự ước mọi người sẽ để lại nhận xét khi họ đánh dấu bạn xuống. Đặc biệt là vì câu trả lời tôi cung cấp là đúng 100%.
JaredPar 13/10/08

Đó không phải là tôi, nhưng không phải All () trả về chỉ đơn giản là một bool cho biết tất cả các mục phù hợp với điều kiện? Và khởi tạo toSearchFor để bảo đảm không có NullReferenceException.
Lucas

Tôi đã chỉnh sửa vấn đề rỗng thành vấn đề tôi định nhập. Có trên tất cả. Điều này đảm bảo hiệu quả rằng tất cả các chuỗi trong toSearchFor đều được chứa trong chuỗi đầu vào.
JaredPar

6
Tôi không biết điều này trả lời câu hỏi như thế nào. Câu hỏi có thay đổi không?
tvanfosson

15

LINQ trong .NET 4.0 có một tùy chọn khác cho bạn; phương thức .Any ();

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

1
Câu trả lời tuyệt vời, biểu thức với Any()All()phương thức rất đơn giản :) Tôi có thể sử dụng t => words.All(w => t.Title.Contains(w)).
rượu là tà ác.

7

Hoặc nếu bạn đã có dữ liệu trong danh sách và thích định dạng Linq khác :)

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

Làm thế nào về:

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

NotSupportedException: Toán tử so sánh không được hỗ trợ cho loại 'System.String []' Cảm ơn nhưng hãy thử lại?
SpoiledTechie.com

+1, nếu đây thực sự là những gì họ muốn. Nó không phải là rất rõ ràng từ câu hỏi.
Lucas

2

Đây là một ví dụ về một cách viết phương thức mở rộng (lưu ý: Tôi sẽ không sử dụng phương thức này cho các mảng rất lớn; một cấu trúc dữ liệu khác sẽ thích hợp hơn ...):

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
sẽ giống với public static bool Chứa (this string [] stringarray, string pat) {return Array.IndexOf (stringarray, pat)! = -1; }
James Curran

5
string [] thực hiện IEnumerable <string>, vì vậy nó đã có một phương thức mở rộng Chứa (string). Tại sao chúng tôi thực hiện lại điều này?
Lucas

2

Đây là một câu trả lời muộn, nhưng tôi tin rằng nó vẫn hữu ích .
Tôi đã tạo gói nuget NinjaNye.SearchExtension có thể giúp giải quyết vấn đề này.:

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

Bạn cũng có thể tìm kiếm nhiều thuộc tính chuỗi

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

Hoặc thực hiện một RankedSearchtrả về IQueryable<IRanked<T>>chỉ bao gồm một thuộc tính hiển thị số lần các cụm từ tìm kiếm đã xuất hiện:

//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ó một hướng dẫn mở rộng hơn trên trang GitHub của các dự án: https://github.com/ninjanye/SearchExtensions

Hy vọng điều này sẽ giúp du khách trong tương lai


1
Vâng, nó được xây dựng đặc biệt để nhắm mục tiêu Entity Framework
NinjaNye

1
Tôi có thể sử dụng điều này với phương thức .Where () không?
Hamza Khanzada

Vâng, nó hoạt động IQueryableIEnumerable- chỉ cần lưu ý rằng nếu bạn chuỗi nó ra khỏi IEnumerable, nó sẽ chạy trong memeory thay vì xây dựng một truy vấn và gửi nó xuống nguồn
NinjaNye

2

Phương thức mở rộng Linq. Sẽ hoạt động với bất kỳ đối tượng IEnumerable nào:

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

Sử dụng:

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

bool Array2ItemsInArray1 = List1.ContainsAny(List2);

1

Tôi tin rằng bạn cũng có thể làm điều gì đó như thế này.

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

Tương tự như "where stringArray.Contains (xx.uid.ToString ())", không cần phải bao bọc nó trong một truy vấn
Lucas

0

Vì vậy, tôi có giả định chính xác rằng uid là Mã định danh duy nhất (Hướng dẫn) không? Đây chỉ là một ví dụ về một trường hợp có thể xảy ra hay bạn đang thực sự cố gắng tìm một hướng dẫn phù hợp với một mảng chuỗi?

Nếu điều này là đúng, bạn có thể muốn thực sự suy nghĩ lại toàn bộ cách tiếp cận này, đây có vẻ là một ý tưởng thực sự tồi. Có lẽ bạn nên cố gắng đối sánh Hướng dẫn với Hướng dẫn

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

Thành thật mà nói, tôi không thể tưởng tượng được một tình huống trong đó kết hợp một mảng chuỗi bằng cách sử dụng "chứa" với nội dung của Hướng dẫn sẽ là một ý tưởng hay. Đối với một điều, Contains () sẽ không đảm bảo thứ tự các số trong Hướng dẫn để bạn có thể khớp nhiều mục. Chưa kể đến việc so sánh các loại cá theo cách này sẽ chậm hơn so với chỉ làm trực tiếp.


0

Bạn nên viết nó theo cách khác, kiểm tra danh sách id người dùng được đặt trước của bạn có chứa id trên hàng bảng đó:

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

LINQ hoạt động khá sáng sủa ở đây và chuyển đổi nó thành một câu lệnh SQL tốt:

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'

về cơ bản nhúng nội dung của mảng 'tìm kiếm' vào truy vấn sql và thực hiện lọc với từ khóa 'IN' trong SQL.


Điều này hoạt động tốt miễn là bạn không có hơn 2100 tham số.
jpierson

0

Tôi đã cố gắng tìm ra một giải pháp, nhưng không phải là một giải pháp tuyệt vời vì nó yêu cầu sử dụng AsEnumerable () sẽ trả về tất cả các kết quả từ DB, may mắn thay, tôi chỉ có 1k bản ghi trong bảng nên nó không thực sự đáng chú ý, nhưng đây là .

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;

Bài viết gốc của tôi như sau:

Làm thế nào để bạn làm điều ngược lại? Tôi muốn làm điều gì đó như sau trong khung thực thể.

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

Những gì tôi muốn là tìm tất cả người dùng mà Tên đầy đủ của họ chứa tất cả các phần tử trong `` tìm kiếm ''. Tôi đã thử một số cách khác nhau, tất cả đều không hiệu quả với tôi.

Tôi cũng đã thử

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

Phiên bản này chỉ tìm những cái có chứa phần tử cuối cùng trong mảng tìm kiếm.


0

Giải pháp tốt nhất mà tôi tìm thấy là tiếp tục và tạo một Hàm có giá trị bảng trong SQL để tạo ra các kết quả, chẳng hạn như ::

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

Sau đó, bạn chỉ cần kéo hàm vào trình thiết kế LINQ.dbml và gọi nó giống như bạn thực hiện các đối tượng khác của mình. LINQ thậm chí còn biết các cột của hàm được lưu trữ của bạn. Tôi gọi nó ra như thế này ::

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

Cực kỳ đơn giản và thực sự phát huy hết sức mạnh của SQL và LINQ trong ứng dụng ... và tất nhiên, bạn có thể tạo bất kỳ hàm có giá trị bảng nào mà bạn muốn cho các hiệu ứng tương tự!


0

Tôi tin rằng điều bạn thực sự muốn làm là: hãy tưởng tượng một tình huống bạn có hai cơ sở dữ liệu và chúng có một bảng sản phẩm chung Và bạn muốn chọn các sản phẩm từ bảng "A" mà id có chung với "B"

sử dụng phương thức chứa sẽ quá phức tạp để thực hiện điều này, những gì chúng ta đang làm là một giao điểm và có một phương thức được gọi là giao điểm cho điều đó

một ví dụ từ msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

int [] number = (0, 2, 4, 5, 6, 8, 9); int [] numberB = (1, 3, 5, 7, 8); var = commonNumbers numberA.Intersect (numberB);

Tôi nghĩ những gì bạn cần sẽ được giải quyết dễ dàng với giao lộ


0

Kiểm tra phương thức mở rộng này:

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

Thử:

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

Mặc dù mã này có thể trả lời câu hỏi, nhưng việc cung cấp thêm ngữ cảnh về cách và / hoặc lý do tại sao nó giải quyết vấn đề sẽ cải thiện giá trị lâu dài của câu trả lời.
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()) 
  }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.