How do I use LINQ Contains(string[]) instead of Contains(string) - c#

I got one big question.
I got a linq query to put it simply looks like this:
from xx in table
where xx.uid.ToString().Contains(string[])
select xx
The values of the string[] array would be numbers like (1,45,20,10,etc...)
the Default for .Contains is .Contains(string).
I need it to do this instead: .Contains(string[])...
EDIT : One user suggested writing an extension class for string[]. I would like to learn how, but any one willing to point me in the right direction?
EDIT : The uid would also be a number. That's why it is converted to a string.
Help anyone?

spoulson has it nearly right, but you need to create a List<string> from string[] first. Actually a List<int> would be better if uid is also int. List<T> supports Contains(). Doing uid.ToString().Contains(string[]) would imply that the uid as a string contains all of the values of the array as a substring??? Even if you did write the extension method the sense of it would be wrong.
[EDIT]
Unless you changed it around and wrote it for string[] as Mitch Wheat demonstrates, then you'd just be able to skip the conversion step.
[ENDEDIT]
Here is what you want, if you don't do the extension method (unless you already have the collection of potential uids as ints -- then just use List<int>() instead). This uses the chained method syntax, which I think is cleaner, and
does the conversion to int to ensure that the query can be used with more providers.
var uids = arrayofuids.Select(id => int.Parse(id)).ToList();
var selected = table.Where(t => uids.Contains(t.uid));

If you are truly looking to replicate Contains, but for an array, here is an extension method and sample code for usage:
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;
}
}
}

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

LINQ in .NET 4.0 has another option for you; the .Any() method;
string[] values = new[] { "1", "2", "3" };
string data = "some string 1";
bool containsAny = values.Any(data.Contains);

Or if you already have the data in a list and prefer the other Linq format :)
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();

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

This is an example of one way of writing an extension method (note: I wouldn't use this for very large arrays; another data structure would be more appropriate...):
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;
}
}
}

This is a late answer, but I believe it is still useful.
I have created the NinjaNye.SearchExtension nuget package that can help solve this very problem.:
string[] terms = new[]{"search", "term", "collection"};
var result = context.Table.Search(terms, x => x.Name);
You could also search multiple string properties
var result = context.Table.Search(terms, x => x.Name, p.Description);
Or perform a RankedSearch which returns IQueryable<IRanked<T>> which simply includes a property which shows how many times the search terms appeared:
//Perform search and rank results by the most hits
var result = context.Table.RankedSearch(terms, x => x.Name, x.Description)
.OrderByDescending(r = r.Hits);
There is a more extensive guide on the projects GitHub page: https://github.com/ninjanye/SearchExtensions
Hope this helps future visitors

Linq extension method. Will work with any IEnumerable object:
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);

I believe you could also do something like this.
from xx in table
where (from yy in string[]
select yy).Contains(xx.uid.ToString())
select xx

So am I assuming correctly that uid is a Unique Identifier (Guid)? Is this just an example of a possible scenario or are you really trying to find a guid that matches an array of strings?
If this is true you may want to really rethink this whole approach, this seems like a really bad idea. You should probably be trying to match a Guid to a Guid
Guid id = new Guid(uid);
var query = from xx in table
where xx.uid == id
select xx;
I honestly can't imagine a scenario where matching a string array using "contains" to the contents of a Guid would be a good idea. For one thing, Contains() will not guarantee the order of numbers in the Guid so you could potentially match multiple items. Not to mention comparing guids this way would be way slower than just doing it directly.

You should write it the other way around, checking your priviliged user id list contains the id on that row of table:
string[] search = new string[] { "2", "3" };
var result = from x in xx where search.Contains(x.uid.ToString()) select x;
LINQ behaves quite bright here and converts it to a good SQL statement:
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'
which basicly embeds the contents of the 'search' array into the sql query, and does the filtering with 'IN' keyword in SQL.

I managed to find a solution, but not a great one as it requires using AsEnumerable() which is going to return all results from the DB, fortunately I only have 1k records in the table so it isn't really noticable, but here goes.
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;
My original post follows:
How do you do the reverse? I want to
do something like the following in
entity framework.
string[] search = new string[] { "John", "Doe" };
var users = from u in ctx.Users
from s in search
where u.FullName.Contains(s)
select u;
What I want is to find all users where
their FullName contains all of the
elements in `search'. I've tried a
number of different ways, all of which
haven't been working for me.
I've also tried
var users = from u in ctx.Users select u;
foreach (string s in search) {
users = users.Where(u => u.FullName.Contains(s));
}
This version only finds those that
contain the last element in the search
array.

The best solution I found was to go ahead and create a Table-Valued Function in SQL that produces the results, such as ::
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')
Then, you simply drag the function into your LINQ.dbml designer and call it like you do your other objects. The LINQ even knows the columns of your stored function. I call it out like this ::
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
Incredibly simple and really utlizes the power of SQL and LINQ in the application...and you can, of course, generate any table valued function you want for the same effects!

I believe that what you really want to do is:
let's imagine a scenario
you have two database
and they have a table of products in common
And you want to select products from the table "A" that id has in common with the "B"
using the method contains would be too complicated to do this
what we are doing is an intersection, and there is a method called intersection for that
an example from msdn:
http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#intersect1
int [] numbers = (0, 2, 4, 5, 6, 8, 9);
int [] numbersB = (1, 3, 5, 7, 8);
var = commonNumbers numbersA.Intersect (numbersB);
I think what you need is easily solved with intersection

Check this extension method:
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;
}
}
}

from xx in table
where xx.uid.Split(',').Contains(string value )
select xx

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

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;

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

string[] stringArray = {1,45,20,10};
from xx in table
where stringArray.Contains(xx.uid.ToString())
select xx

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

Related

Search a List of string array to find a value in matching element and return another element in same array

So I have
List<string[]> listy = new List<string[]>();
listy.add('a','1','blue');
listy.add('b','2','yellow');
And i want to search through all of the list ti find the index where the array containing 'yellow' is, and return the first element value, in this case 'b'.
Is there a way to do this with built in functions or am i going to need to write my own search here?
Relatively new to c# and not aware of good practice or all the built in functions. Lists and arrays im ok with but lists of arrays baffles me somewhat.
Thanks in advance.
As others have already suggested, the easiest way to do this involves a very powerful C# feature called LINQ ("Language INtegrated Queries). It gives you a SQL-like syntax for querying collections of objects (or databases, or XML documents, or JSON documents).
To make LINQ work, you will need to add this at the top of your source code file:
using System.Linq;
Then you can write:
IEnumerable<string> yellowThings =
from stringArray in listy
where stringArray.Contains("yellow")
select stringArray[0];
Or equivalently:
IEnumerable<string> yellowThings =
listy.Where(strings => strings.Contains("yellow"))
.Select(strings => strings[0]);
At this point, yellowThings is an object containing a description of the query that you want to run. You can write other LINQ queries on top of it if you want, and it won't actually perform the search until you ask to see the results.
You now have several options...
Loop over the yellow things:
foreach(string thing in yellowThings)
{
// do something with thing...
}
(Don't do this more than once, otherwise the query will be evaluated repeatedly.)
Get a list or array :
List<string> listOfYellowThings = yellowThings.ToList();
string[] arrayOfYellowThings = yellowThings.ToArray();
If you expect to have exactly one yellow thing:
string result = yellowThings.Single();
// Will throw an exception if the number of matches is zero or greater than 1
If you expect to have either zero or one yellow things:
string result = yellowThings.SingleOrDefault();
// result will be null if there are no matches.
// An exception will be thrown if there is more than one match.
If you expect to have one or more yellow things, but only want the first one:
string result = yellowThings.First();
// Will throw an exception if there are no yellow things
If you expect to have zero or more yellow things, but only want the first one if it exists:
string result = yellowThings.FirstOrDefault();
// result will be null if there are no yellow things.
Based on the problem explanation provided by you following is the solution I can suggest.
List<string[]> listy = new List<string[]>();
listy.Add(new string[] { "a", "1", "blue"});
listy.Add(new string[] { "b", "2", "yellow"});
var target = listy.FirstOrDefault(item => item.Contains("yellow"));
if (target != null)
{
Console.WriteLine(target[0]);
}
This should solve your issue. Let me know if I am missing any use case here.
You might consider changing the data structure,
Have a class for your data as follows,
public class Myclas
{
public string name { get; set; }
public int id { get; set; }
public string color { get; set; }
}
And then,
static void Main(string[] args)
{
List<Myclas> listy = new List<Myclas>();
listy.Add(new Myclas { name = "a", id = 1, color = "blue" });
listy.Add(new Myclas { name = "b", id = 1, color = "yellow" });
var result = listy.FirstOrDefault(t => t.color == "yellow");
}
Your current situation is
List<string[]> listy = new List<string[]>();
listy.Add(new string[]{"a","1","blue"});
listy.Add(new string[]{"b","2","yellow"});
Now there are Linq methods, so this is what you're trying to do
var result = listy.FirstOrDefault(x => x.Contains("yellow"))?[0];

C# - Nested Array/Data structures

Recently, I have been getting into C# (ASP.NET) and moving on from PHP. I want to achieve something like this:
mainArray (
array 1 (
'name' => 'example'
),
array 2 (
'name' => 'example2'
)
);
I know that you can use an Array in C# however, you must indicate the length of the Array before doing so which is where the problem is.
I want to loop through a Database in a Class function which returns an Array of all the columns, ie:
id, username, email.
I have tried:
public Array search_bustype(string match, string forthat)
{
db = new rkdb_07022016Entities2();
var tbl = (from c in db.tblbus_business select c).ToArray();
List<string> List = new List<string>();
int i = 0;
foreach (var toCheck in tbl)
{
if (toCheck.BusType.ToString() == match)
{
if (forthat == "Name")
{
List.Add(toCheck.Name);
}
if (forthat == "Address")
{
}
}
i++;
}
return List.ToArray();
}
But as you can see, I am having to only return the single column because the List is not multidimensional (can't be nested).
What can I use to solve this issue? I have looked at some links:
C# Arrays
StackOverflow post
But these again are an issue for my structure since I don't know how many index's I need in the Array when declaring it - The Database grows everyday.
Thanks in advance.
Try something like this. First, define a class for your business model.
public class Person
{
public string Name {get;set;}
public string Address {get;set;}
}
Then use a generic list instead of a string list.
public Person[] search_bustype(string match, string forthat)
{
var db = new rkdb_07022016Entities2();
List<Person> personList = new List<Person>();
foreach (var toCheck in db.tblbus_business.Where(b => b.BusType.ToString() == match))
{
var model = new Person { Name = toCheck.Name, Address = toCheck.Address };
personList.Add(model);
}
return personList.ToArray();
}
I'm not sure what you are trying to do with the forthat variable.
You can use a list of lists
IList<IList<string>> multiList;

Split list values into comma separated strings

I am picking distinct values from a datatable column like below.
var uniqueCC = dtNew.AsEnumerable().Select(s => new { cc = s.Field<string>("ID"), }).Distinct().ToList();
var uniqueCode = dtNew.AsEnumerable().Select(s => new { Code = s.Field<string>("EAI"), }).Distinct().ToList();
Now I need to get the values alone in a comma seperated string and I'm using the below code and it doesn't take the value alone.
string strCC = String.Join(",", uniqueCC);
string strEAI = String.Join(",", uniqueCode);
Please provide some suggestions.
List values
cc=1, cc=2, cc=3
Expected Result
1,2,3
You can just use LINQ Select() method to pass the value alone to String.Join() :
string strCC = String.Join(",", uniqueCC.Select(o => o.cc));
Or just return string values instead of anonymous type in the first place :
var uniqueCC = dtNew.AsEnumerable().Select(s => s.Field<string>("ID")).Distinct();
string strCC = String.Join(",", uniqueCC);
You can use aggregate functions available in c#.
Let say you have a list of string called listOfString, then you can call the aggregate function like this.
string outString = listOfString.Aggregate((a, b) => a + "," + b);
It will do the trick.
As an extension to har07's excellent answer, if this is something you do a lot of the time, to save writing loads of code you could implement this as a static extension method, where you pass the datatype, the column name and your required separator - so it could handle different column datatypes and separators - in a separate file:
namespace Extensions
{
public static class LinqExtensions
{
public static String ReturnSeparatedString<T>(this DataTable datatable, string field, string separator)
{
var unique =
datatable.AsEnumerable()
.Select(s => new {cc = s.Field<string>(field),}).Distinct();
return String.Join(separator, unique.Select(o => o.cc));
}
}
}
Then call it from your code by creating a reference to your new Extensions.LinqExtensions class, and calling it directly on the dataTable like this:
var commaSeparatedIds = dataTable.ReturnSeparatedString<string>("ID",",");
var commaSeparatedEAIs = dataTable.ReturnSeparatedString<string>("EAI",",");
var commaSeparatedInts = dataTable.ReturnSeparatedString<int>("MYINTS",",");
var dotSeparatedStrings = dataTable.ReturnSeparatedString<int>("OtherId", ".");

Can I select individual Items from a list?

I have a list of strings and I need to select certain parts of the list to construct a separate string. What I have is:
name,gender,haircolour,car;
or
John,Male,Brown,toyota;
I also have a separate file indicating which parts, and in what order the new string should be constructed.
eg: Index = 0,3,1 would print John,toyota,Male or 1,2,0 would print Male,Brown,John.
I have tried several methods to try and select the index of the items I want, but all the functions that return values only return the contents of the List, and the only return that gives an integer is Count(), which I can't see as being helpful.
I have tried and tried but all I have succeeded in doing is confusing myself more and more. Can anyone help suggest a way to achieve this?
You should be able to do list[i], where i is the index of the element you need. There are some examples here: http://www.dotnetperls.com/list
List<string> list = new List<string> { "John", "Male", "Brown", "toyota" };
List<int> indexList = new List<int> { 0, 3, 1 };
List<string> resultList = new List<string>();
indexList.ForEach(i => resultList.Add(list[i]));
If I am understanding the question correctly, something like this should do the job:
const string data = "John,Male,Brown,toyota";
const string order = "0,3,1";
string[] indexes = order.Split(',');
string result = indexes.Aggregate("", (current, index) => current + data.Split(',')[int.Parse(index)] + ",");
result = result.TrimEnd(',');
If your string data ends with a semicolon ';', as possibly indicated in your question, then change the line to this:
string result = indexes.Aggregate("", (current, index) => current + data.Split(',')[int.Parse(index)].TrimEnd(';') + ",");
Note this solution doesn't check to make sure that the given index exists in the given data string. To add a check to make sure the index doesn't go over the array bounds, do something like this instead:
string result = indexes.Where(z => int.Parse(z) < data.Split(',').Length).Aggregate("", (current, index) => current + data.Split(',')[int.Parse(index)].TrimEnd(';') + ",");

Regex Replace to assist Orderby in LINQ

I'm using LINQ to SQL to pull records from a database, sort them by a string field, then perform some other work on them. Unfortunately the Name field that I'm sorting by comes out of the database like this
Name
ADAPT1
ADAPT10
ADAPT11
...
ADAPT2
ADAPT3
I'd like to sort the Name field in numerical order. Right now I'm using the Regex object to replace "ADAPT1" with "ADAPT01", etc. I then sort the records again using another LINQ query. The code I have for this looks like
var adaptationsUnsorted = from aun in dbContext.Adaptations
where aun.EventID == iep.EventID
select new Adaptation
{
StudentID = aun.StudentID,
EventID = aun.EventID,
Name = Regex.Replace(aun.Name,
#"ADAPT([0-9])$", #"ADAPT0$1"),
Value = aun.Value
};
var adaptationsSorted = from ast in adaptationsUnsorted
orderby ast.Name
select ast;
foreach(Adaptation adaptation in adaptationsSorted)
{
// do real work
}
The problem I have is that the foreach loop throws the exception
System.NotSupportedException was unhandled
Message="Method 'System.String Replace(System.String, System.String,
System.String)' has no supported translation to SQL."
Source="System.Data.Linq"
I'm also wondering if there's a cleaner way to do this with just one LINQ query. Any suggestions would be appreciated.
Force the hydration of the elements by enumerating the query (call ToList). From that point on, your operations will be against in-memory objects and those operations will not be translated into SQL.
List<Adaptation> result =
dbContext.Adaptation
.Where(aun => aun.EventID = iep.EventID)
.ToList();
result.ForEach(aun =>
aun.Name = Regex.Replace(aun.Name,
#"ADAPT([0-9])$", #"ADAPT0$1")
);
result = result.OrderBy(aun => aun.Name).ToList();
Implement a IComparer<string> with your logic:
var adaptationsUnsorted = from aun in dbContext.Adaptations
where aun.EventID == iep.EventID
select new Adaptation
{
StudentID = aun.StudentID,
EventID = aun.EventID,
Name = aun.Name,
Value = aun.Value
};
var adaptationsSorted = adaptationsUnsorted.ToList<Adaptation>().OrderBy(a => a.Name, new AdaptationComparer ());
foreach (Adaptation adaptation in adaptationsSorted)
{
// do real work
}
public class AdaptationComparer : IComparer<string>
{
public int Compare(string x, string y)
{
string x1 = Regex.Replace(x, #"ADAPT([0-9])$", #"ADAPT0$1");
string y1 = Regex.Replace(y, #"ADAPT([0-9])$", #"ADAPT0$1");
return Comparer<string>.Default.Compare(x1, y1);
}
}
I didn't test this code but it should do the job.
I wonder if you can add a calculated+persisted+indexed field to the database, that does this for you. It would be fairly trivial to write a UDF that gets the value as an integer (just using string values), but then you can sort on this column at the database. This would allow you to use Skip and Take effectively, rather than constantly fetching all the data to the .NET code (which simply doesn't scale).

Categories