ฉันจะใช้ LINQ ประกอบด้วย (สตริง []) แทนประกอบด้วย (สตริง) ได้อย่างไร


102

ฉันมีคำถามใหญ่ข้อหนึ่ง

ฉันได้รับแบบสอบถาม linq ที่จะทำให้ดูเหมือนว่า:

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

ค่าของstring[]อาร์เรย์จะเป็นตัวเลขเช่น (1,45,20,10 ฯลฯ ... )

เริ่มต้นสำหรับการมี.Contains.Contains(string)

ฉันต้องการให้ทำสิ่งนี้แทน: .Contains(string[])...

แก้ไข:ผู้ใช้รายหนึ่งแนะนำให้เขียนคลาสส่วนขยายสำหรับstring[]. ฉันต้องการเรียนรู้วิธีการ แต่มีใครยินดีที่จะชี้ให้ฉันไปในทิศทางที่ถูกต้อง?

แก้ไข: uid จะเป็นตัวเลขด้วย นั่นเป็นเหตุผลว่าทำไมจึงถูกแปลงเป็นสตริง

ช่วยใคร?


คุณต้องชี้แจงว่า uid อาจมีลักษณะอย่างไรและสิ่งที่จะถือว่าตรงกัน
James Curran

3
ตัวอย่างจะดี สำหรับฉันดูเหมือนคำถามจะถาม UID เช่น CA1FAB689C33 และอาร์เรย์เช่น: {"42", "2259", "CA"}
Thomas Bratt

3
ตรงข้ามทำให้รู้สึกมากกว่า: string [] ประกอบด้วย (xx.uid)
majkinetor

คำตอบ:


86

spoulson มีมันเกือบขวา แต่คุณจำเป็นต้องสร้างList<string>จากstring[]ครั้งแรก จริงList<int>จะดีกว่าถ้า uid intนี้ยังมี สนับสนุนList<T> Contains()การทำเช่น uid.ToString().Contains(string[])นี้หมายความว่า uid เป็นสตริงมีค่าทั้งหมดของอาร์เรย์เป็นสตริงย่อย ??? แม้ว่าคุณจะเขียนวิธีการขยาย แต่ความรู้สึกก็จะผิด

[แก้ไข]

เว้นแต่ว่าคุณจะเปลี่ยนและเขียนมันstring[]ตามที่ Mitch Wheat แสดงให้เห็นคุณก็สามารถข้ามขั้นตอนการแปลงได้

[ENDEDIT]

นี่คือสิ่งที่คุณต้องการหากคุณไม่ได้ใช้วิธีการขยาย (เว้นแต่คุณจะมีคอลเล็กชัน uids ที่เป็นไปได้เป็น ints อยู่แล้วให้ใช้List<int>()แทน) สิ่งนี้ใช้ไวยากรณ์วิธีการล่ามโซ่ซึ่งฉันคิดว่าสะอาดกว่าและทำการแปลงเป็น int เพื่อให้แน่ใจว่าสามารถใช้แบบสอบถามกับผู้ให้บริการได้มากขึ้น

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

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

ขอบคุณ. มันเป็นคำตอบที่ถูกต้อง ... อีกหนึ่งความคิด? สมมติว่า arrayuids เป็นแบบสอบถาม linq ด้วย วิธีใดที่คุณสามารถทำให้ทั้งสองคำสั่งลงเหลือเพียงแบบสอบถามเดียวจากฐานข้อมูล
SpoiledTechie.com

4
ตาม MSDN สตริง [] ใช้ IEnumerable <T> ซึ่งมีวิธีการประกอบด้วย ดังนั้นจึงไม่จำเป็นต้องแปลงอาร์เรย์เป็น IList <T> msdn.microsoft.com/en-us/library/19e6zeyy.aspx
spoulson

สุดท้าย. ToString () ส่งข้อผิดพลาดให้ฉัน โดยเฉพาะ LINQ to Entities ไม่รู้จักเมธอด 'System.String ToString ()' และวิธีนี้ไม่สามารถแปลเป็นนิพจน์ร้านค้าได้ .... หลังจากลบแลมบ์ดาก็ใช้ได้สำหรับฉัน
Sam Stange

ฉันรักสิ่งนี้มันง่ายมากจนฉันจำมันไม่ได้เลย
Olaj

@SamStange - ปัญหาอย่างหนึ่งของ LINQ คือมีตัวแปรมากมายและสิ่งที่เป็นนามธรรมนั้น "รั่ว" บางครั้งคุณจำเป็นต้องรู้ว่าคุณกำลังใช้ตัวแปรใดเพื่อสร้างแบบสอบถามได้อย่างถูกต้อง ตามที่เขียนไว้สิ่งนี้จะใช้ได้กับ LINQ ไปยังวัตถุ (และอาจเป็น LINQ ถึง SQL) สำหรับ EF คุณจะทำในทางกลับกันและสร้างคอลเล็กชันในหน่วยความจำList<int>แทนและข้ามการToStringโทร
tvanfosson

37

หากคุณกำลังมองหาอย่างแท้จริงที่จะทำซ้ำมีแต่สำหรับอาร์เรย์ที่นี่เป็นวิธีขยายและรหัสตัวอย่างสำหรับการใช้งาน:

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 คุณควรส่งสิ่งนี้ไปที่ExtensionMethod.netโดยสิ้นเชิง ขอบคุณสำหรับรหัสที่ยอดเยี่ยมมันช่วยแก้ปัญหาของฉันได้แล้ววันนี้!
p.campbell

4
ฉันคิดว่าคุณหมายถึง! string.IsNullOrEmpty (str) && values.Length> 0
Greg Bogumil

คุณพูดถูก ฉันเปลี่ยนมันแม้ว่ามันจะไม่มีผลกระทบต่อการทำงาน ฉันใช้ฟังก์ชันเช่นนี้ในที่ทำงาน ฉันจะต้องตรวจสอบ!
Jason Jackson

@JasonJackson ฉันรู้ว่านี่มันเก่าแล้ว แต่ (ตามที่ Greg พูดถึง) คุณไม่ต้องการ "และ" มากกว่า "หรืออื่น ๆ " ในเงื่อนไขนั้นหรือไม่?
Tieson T.

@TiesonT. ทั้งเงื่อนไข "หรืออื่น ๆ " และ "และ" จะจบลงด้วยผลลัพธ์เดียวกันที่ส่งคืนโดยฟังก์ชัน หากการ!string.IsNullOrEmpty(str)ตรวจสอบผ่านทำให้values.Length > 0เงื่อนไขถูกละเว้นแต่ความยาวของค่าเป็น 0ดังนั้นมันจะไปที่ค่านี้foreachแล้วแตกทันทีเนื่องจากไม่มีรายการในอาร์เรย์โดยไปที่return false.
Meowmaritus

20

ลองทำดังต่อไปนี้

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

2
ฉันหวังว่าผู้คนจะแสดงความคิดเห็นเมื่อพวกเขาทำเครื่องหมายคุณ โดยเฉพาะอย่างยิ่งเนื่องจากคำตอบที่ฉันให้นั้นถูกต้อง 100%
JaredPar

ไม่ใช่ฉัน แต่ All () ไม่ส่งคืนเพียงบูลที่ระบุว่ารายการทั้งหมดตรงกับเงื่อนไขตรงไหน? และการเตรียมใช้งาน toSearchFor เพื่อเป็นค่าว่างเพื่อรับประกัน NullReferenceException
Lucas

ฉันแก้ไขปัญหา null เป็นสิ่งที่ฉันตั้งใจจะพิมพ์ ใช่ทั้งหมด สิ่งนี้ช่วยให้มั่นใจได้อย่างมีประสิทธิภาพว่าสตริงทั้งหมดใน toSearchFor มีอยู่ภายในสตริงอินพุต
JaredPar

6
ฉันไม่เห็นว่าสิ่งนี้ตอบคำถามได้อย่างไร คำถามเปลี่ยนไปสำหรับคุณหรือไม่?
tvanfosson

15

LINQ ใน. NET 4.0 มีอีกทางเลือกหนึ่งสำหรับคุณ วิธี. Any ();

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

1
คำตอบที่ดีการแสดงออกAny()และAll()วิธีการนั้นง่ายมาก :) ฉันสามารถt => words.All(w => t.Title.Contains(w))ใช้ได้
แอลกอฮอล์เป็นสิ่งชั่วร้าย

7

หรือหากคุณมีข้อมูลในรายการอยู่แล้วและชอบรูปแบบ 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

เกี่ยวกับ:

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

NotSupportedException: ตัวดำเนินการเปรียบเทียบไม่รองรับประเภท 'System.String []' ขอบคุณ แต่ลองอีกครั้ง?
SpoiledTechie.com

+1 หากนี่คือสิ่งที่พวกเขาต้องการจริงๆ มันไม่ชัดเจนมากจากคำถาม
Lucas

2

นี่คือตัวอย่างวิธีหนึ่งในการเขียนวิธีการขยาย (หมายเหตุ: ฉันจะไม่ใช้สิ่งนี้สำหรับอาร์เรย์ที่มีขนาดใหญ่มากโครงสร้างข้อมูลอื่นจะเหมาะสมกว่า ... ):

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
ที่จะเหมือนกับบูลแบบคงที่สาธารณะประกอบด้วย (สตริงนี้ [] stringarray, สตริงแพท) {return Array.IndexOf (stringarray, pat)! = -1; }
James Curran

5
string [] ใช้ IEnumerable <string> ดังนั้นจึงมีวิธีการขยายประกอบด้วย (string) อยู่แล้ว เหตุใดเราจึงนำสิ่งนี้มาใช้ใหม่
Lucas

2

นี่คือคำตอบที่ปลาย แต่ผมเชื่อว่ามันยังคงมีประโยชน์
เราได้สร้างNinjaNye.SearchExtensionแพคเกจ nuget ที่จะช่วยให้สามารถแก้ปัญหานี้มาก .:

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

คุณยังสามารถค้นหาคุณสมบัติสตริงหลายรายการ

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

หรือดำเนินการRankedSearchที่ส่งคืนIQueryable<IRanked<T>>ซึ่งรวมถึงคุณสมบัติที่แสดงจำนวนครั้งที่ข้อความค้นหาปรากฏ:

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

มีคำแนะนำที่ครอบคลุมมากขึ้นในหน้าโครงการ GitHub: https://github.com/ninjanye/SearchExtensions

หวังว่านี่จะช่วยผู้เยี่ยมชมในอนาคต


1
ใช่มันถูกสร้างขึ้นโดยเฉพาะเพื่อกำหนดเป้าหมาย Entity Framework
NinjaNye

1
ฉันสามารถใช้สิ่งนี้กับเมธอด. Where () ได้หรือไม่
Hamza Khanzada

ใช่มันใช้งานได้IQueryableและIEnumerable- อย่าเพิ่งว่าถ้าคุณเชื่อมต่อกับ IE ที่ไม่สามารถนับได้มันจะทำงานในหน่วยความจำแทนที่จะสร้างแบบสอบถามและส่งไปยังแหล่งที่มา
NinjaNye

2

วิธีการขยาย Linq จะทำงานร่วมกับอ็อบเจ็กต์ IEnumerable:

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

การใช้งาน:

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

bool Array2ItemsInArray1 = List1.ContainsAny(List2);

1

ฉันเชื่อว่าคุณสามารถทำอะไรแบบนี้ได้

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

เช่นเดียวกับ "ที่ stringArray.Contains (xx.uid ToString ())" ไม่จำเป็นต้องรวมไว้ในแบบสอบถาม
Lucas

0

ดังนั้นฉันถือว่าถูกต้องหรือไม่ว่า uid เป็น Unique Identifier (Guid) นี่เป็นเพียงตัวอย่างของสถานการณ์ที่เป็นไปได้หรือคุณกำลังพยายามค้นหา guid ที่ตรงกับอาร์เรย์ของสตริง

หากเป็นความจริงคุณอาจต้องคิดทบทวนวิธีการทั้งหมดนี้ใหม่ดูเหมือนเป็นความคิดที่แย่จริงๆ คุณน่าจะพยายามจับคู่ Guid กับ Guid

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

ฉันนึกภาพไม่ออกจริงๆว่าการจับคู่สตริงอาร์เรย์โดยใช้ "มี" กับเนื้อหาของ Guid นั้นเป็นความคิดที่ดี ประการหนึ่งคือมี () จะไม่รับประกันลำดับของตัวเลขใน Guid ดังนั้นคุณจึงสามารถจับคู่หลายรายการได้ ไม่ต้องพูดถึงการเปรียบเทียบแนวทางด้วยวิธีนี้จะช้ากว่าการทำโดยตรง


0

คุณควรเขียนในทางกลับกันการตรวจสอบรายการ id ผู้ใช้ที่เป็นส่วนตัวของคุณมี id ในแถวนั้นของตาราง:

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

LINQ ทำงานได้ค่อนข้างสดใสที่นี่และแปลงเป็นคำสั่ง 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'

ซึ่งโดยพื้นฐานแล้วจะฝังเนื้อหาของอาร์เรย์ 'การค้นหา' ลงในแบบสอบถาม sql และทำการกรองด้วยคำหลัก 'IN' ใน SQL


สิ่งนี้ใช้ได้ดีตราบเท่าที่คุณไม่มีพารามิเตอร์มากกว่า 2100 ตัว
jpierson

0

ฉันจัดการเพื่อหาวิธีแก้ปัญหา แต่ไม่ใช่วิธีที่ดีเพราะต้องใช้ AsEnumerable () ซึ่งจะส่งคืนผลลัพธ์ทั้งหมดจากฐานข้อมูลโชคดีที่ฉันมีระเบียน 1k ในตารางเท่านั้นดังนั้นจึงไม่เป็นที่สังเกตได้ แต่นี่ไป .

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;

โพสต์เดิมของฉันมีดังนี้:

คุณทำย้อนกลับได้อย่างไร? ฉันต้องการทำสิ่งต่อไปนี้ในกรอบงานเอนทิตี

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

สิ่งที่ฉันต้องการคือค้นหาผู้ใช้ทั้งหมดโดยที่ FullName มีองค์ประกอบทั้งหมดใน `` การค้นหา '' ฉันได้ลองหลายวิธีแล้วซึ่งทั้งหมดนี้ยังไม่ได้ผล

ฉันยังพยายาม

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

เวอร์ชันนี้จะค้นหาเฉพาะที่มีองค์ประกอบสุดท้ายในอาร์เรย์การค้นหา


0

ทางออกที่ดีที่สุดที่ฉันพบคือดำเนินการต่อและสร้างฟังก์ชัน Table-Valued ใน SQL ที่สร้างผลลัพธ์เช่น ::

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

จากนั้นคุณเพียงลากฟังก์ชั่นดังกล่าวไปยังตัวออกแบบ LINQ.dbml ของคุณและเรียกมันว่าคุณทำวัตถุอื่น ๆ ของคุณ LINQ ยังรู้คอลัมน์ของฟังก์ชันที่จัดเก็บของคุณ ฉันเรียกมันออกมาแบบนี้ ::

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

ง่ายอย่างไม่น่าเชื่อและใช้พลังของ SQL และ LINQ ในแอปพลิเคชัน ... และแน่นอนคุณสามารถสร้างฟังก์ชันมูลค่าตารางใด ๆ ที่คุณต้องการสำหรับเอฟเฟกต์เดียวกัน!


0

ฉันเชื่อว่าสิ่งที่คุณต้องการทำจริงๆคือลองนึกภาพสถานการณ์คุณมีฐานข้อมูลสองฐานข้อมูลและมีตารางผลิตภัณฑ์ที่เหมือนกันและคุณต้องการเลือกผลิตภัณฑ์จากตาราง "A" ที่ id มีเหมือนกันกับ "B"

การใช้วิธีการประกอบด้วยจะซับซ้อนเกินไปที่จะทำสิ่งที่เรากำลังทำอยู่คือจุดตัดและมีวิธีที่เรียกว่าจุดตัดสำหรับสิ่งนั้น

ตัวอย่างจาก msdn: http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1

int [] ตัวเลข = (0, 2, 4, 5, 6, 8, 9); int [] numbersB = (1, 3, 5, 7, 8); var = ตัวเลข commonNumbersA.Intersect (numbersB);

ฉันคิดว่าสิ่งที่คุณต้องการสามารถแก้ไขได้อย่างง่ายดายด้วยจุดตัด


0

ตรวจสอบวิธีการขยายนี้:

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

ลอง:

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

แม้ว่ารหัสนี้อาจตอบคำถามได้ แต่การให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและ / หรือเหตุผลในการแก้ปัญหาจะช่วยเพิ่มมูลค่าในระยะยาวของคำตอบ
ระบบ

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();

เรามาที่ SO โปรดอย่าให้คำตอบ "รหัสเท่านั้น" คุณช่วยเพิ่มคำอธิบายได้ไหมว่าวิธีนี้แก้ปัญหาได้อย่างไรและอีก 21 คำตอบยังไม่ครอบคลุม
marsh-wiggle


-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()) 
  }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.