Cannot find the lambda linq equivalent to SELECT * FROM [Source] WHERE [Field] IN [String Array]. I need to select all data from a data table that contains zip codes from a string array. I would like a faster way than just iterating through every row comparing them as I believe this would be fairly inefficient (or I believe it would anyway). I can't seem to find an adequate answer on Google of how to perform a lambda LINQ IN query on a data table. Any assistance would be great! Here is what I have currently:
List<string> lst = dtEtechZipCodeEmailRecipients.AsEnumerable()
.Select(o => o.Field<string>("Email")).Distinct().ToList();
for (int i = 0; i < lst.Count - 1; ++i)
{
string email = lst[i].ToString().ToUpper();
string[] zipCodes = dtEtechZipCodeEmailRecipients.AsEnumerable()
.Where(zip => (zip.Field<string>("Email") ?? (object)String.Empty).ToString().ToUpper() == email)
.Select(zip => zip.Field<string>("ZipCode")).ToArray();
Console.WriteLine(" - " + email);
dtEtechModelRequests.AsEnumerable().Where(mod => mod.Field<string>("ZipCode").Contains(zipCodes)).Select(mod => mod);
}
That does not work, everything but the .Contains does do exactly what I need though. I left the .Contains to try and demonstrate my point.
You should do opposite thing - check whether array of zip codes contains current zip code:
Where(mod => zipCodes.Contains(mod.Field<string>("ZipCode"))
That is same as verifying if current zip code IN array.
Simple answer to your question is,
[Source].Where(el => [String Array].Contains(el.Field<string>("Field"))
And if you need NOT IN then follow the following pattern
Adding ! infront of your [String Array]
[Source].Where(el => ![String Array].Contains(el.Field<string>("Field"))
alternative representation
[Source].Where(el => [String Array].Contains(el.Field<string>("Field")==false)
Related
i hope someone can helpme
im working in c# with entity framework 6
i need to search in a "DESCRIPTION" field of my table "PRODUCTS" the value of textbox, but the text by separate in any order, i can explain better with my code:
string[] palabras = textBox1.Text.Split(' ');
string palabraABuscar = "";
for (int i = 0; i <= palabras.Length - 1; i++)
{
palabraABuscar = palabras[i];
query = query.Where(a => a.DESCRIPCION.Contains(palabraABuscar));
}
the idea is if the user enter "lala1 lala2" the program filter all the products that contains that two words, example:
the program must show the products "CAMERA lala2 with lala1" and "lala1 BOX lala2"
the program must not show "Computer lala1" either "shower lala2"
With my code the program only filter the products by the last word ("lala"), maybe replace the first where, i use a for cicle because i dont know how many
Your currenct code should work as expected, beacuse Where method adds each query with and. Or you can try another option (#Ghasan also stated in comments):
string[] palabras = textBox1.Text.Split(' ');
query = query.Where(a => palabras.All(p => a.DESCRIPCION.Contains(p)));
If nor your current code, nor this works for you, then check if you correctly split data and be sure that query is correct until you reach above code. Additionally you can check generated SQL query like:
context.Database.Log = Console.WriteLine; // Or something similar.
Just replace
query = query.Where(a => a.DESCRIPCION.Contains(palabraABuscar));
with
query = query.Where(a => palabraABuscar.All(x => a.DESCRIPCION.Contains(x));
I have a simple custom table with a search/filter field. I leave the implementation of the search up to each use of the table.
So let's say I have users in my table and I want to search for them. I want to search both in users firstname, lastname and also any role they are in.
This would probably do the trick
searchString = searchString.ToLower();
query = query.Where(
x =>
x.FirstName.ToLower().Contains(searchString)
||
x.LastName.ToLower().Contains(searchString)
||
x.Roles.Any(
role =>
role.Name.ToLower().Contains(searchString)
)
);
But now I want to search/filter on multiple words. First I get an array of all separate words.
var searchStrings = searchString.ToLower().Split(null);
I tried the following but it does not fulfill my requirements listed further down as it returns any user where any word is matched in any field. I need that all words are matched (but possibly in different fields). Se below for more details.
query = query.Where(
x =>
searchStrings.Any(word => x.FirstName.ToLower().Contains(word))
||
searchStrings.Any(word => x.LastName.ToLower().Contains(word))
//snipped away roles search for brevity
);
First let me produce some data
Users (data)
Billy-James Carter is admin and manager
James Carter is manager
Billy Carter has no role
Cases
If my search string is "billy car" I want Billy-James and Billy returned but not James Carter (so all words must match but not on same field).
If my search string is "bil jam" or even "bil jam car" I only want Billy-James returned as he is the only one matching all terms/words. So in this the words bil and jam were both found in the FirstName field while the car term was found in the LastName field. Only getting the "car" part correct is not enough and James is not returned.
If I search for "car man" Billy-James and James are both managers (man) and named Carter and should show up. should I search for "car man admi" then only Billy-James should show up.
I am happy to abandon my current approach if better is suggested.
I cannot think of a way to wrap what you're looking for up into a single LINQ statement. There may be a way, but I know with EF the options are more limited than LINQ on an object collection. With that said, why not grab a result set from the database with the first word in the split, then filter the resulting collection further?
var searchWords = searchString.ToLower().split(' ');
var results = dataBase.Where(i => i.FirstName.ToLower().Contains(searchWords[0])
|| i.LastName.ToLower().Contains(searchWords[0])
|| i.Role.ToLower().Contains(searchWords[0]));
if(searchWords.Length > 1) {
for(int x = 1; x < searchWords.Length; x++) {
results = results.Where(i => i.FirstName.ToLower().Contains(searchWords[x])
|| i.LastName.ToLower().Contains(searchWords[x])
|| i.Role.ToLower().Contains(searchWords[x]));
}
}
Your final content of the results collection will be what you're looking for.
Disclaimer: I didn't have a setup at the ready to test this, so there may be something like a .ToList() needed to make this work, but it's basically functional.
Update: More information about EF and deferred execution, and string collection search
Given we have the schema:
Employee:
FirstName - String
Last Name - String
Roles - One to Many
Role:
Name - String
The following will build a query for everything you want to find
var searchTerms = SearchString.ToLower().Split(null);
var term = searchTerms[0];
var results = from e in entities.Employees
where (e.FirstName.Contains(term)
|| e.LastName.Contains(term)
|| e.Roles.Select(r => r.Name).Any(n => n.Contains(term)))
select e;
if (searchTerms.Length > 1)
{
for (int i = 1; i < searchTerms.Length; i++)
{
var tempTerm = searchTerms[i];
results = from e in results
where (e.FirstName.Contains(tempTerm)
|| e.LastName.Contains(tempTerm)
|| e.Roles.Select(r => r.Name).Any(n => n.Contains(tempTerm)))
select e;
}
}
At this point the query still has not been executed. As you filter the result set in the loop, this is actually adding additional AND clauses to the search criteria. The query doesn't execute until you run a command that does something with the result set like ToList(), iterating over the collection, etc. Put a break point after everything that builds the query and take a look at it. LINQ to SQL is both interesting and powerful.
More on deferred execution
The one thing which needs explanation is the variable tempTerm. We need a variable which is scoped within the loop so that we don't end up with one value for all the parameters in the query referencing the variable term.
I simplified it a bit
//we want to search/filter
if (!string.IsNullOrEmpty(request.SearchText))
{
var searchTerms = request.SearchText.ToLower().Split(null);
foreach (var term in searchTerms)
{
string tmpTerm = term;
query = query.Where(
x =>
x.Name.ToLower().Contains(tmpTerm)
);
}
}
I build a much bigger query where searching is just a part, starting like this
var query = _context.RentSpaces.Where(x => x.Property.PropertyId == request.PropertyId).AsQueryable();
above search only uses one field but should work just fine with more complex fields. like in my user example.
I usually take the apporach to sort of queue the queries. They are all executed in one step at the database if you look with the diagnostic tools:
IQueryable<YourEntity> entityQuery = context.YourEntity.AsQueryable();
foreach (string term in serchTerms)
{
entityQuery = entityQuery.Where(a => a.YourProperty.Contains(term));
}
I have to select distinct records from my simple mongo db database.
I have many simple records these records looks like this :
{"word":"some text"}
My code is very simple.
const string connectionString = "mongodb://localhost";
var client = new MongoClient(connectionString);
MongoServer server = client.GetServer();
MongoDatabase database = server.GetDatabase("text8");
MongoCollection<Element> collection = database.GetCollection<Element>("text8");
MongoCursor<Element> words = (MongoCursor<Element>)collection.FindAll();
But I have't idea how to select distinct word's from database.
Could someone can give me some advice ?
MongoDB API has a distinct aggregation command, which returns distinct values found for a specified key in a collection. You can also use it from C# Driver:
var distinctWords = collection.Distinct("word");
where collection - is an instance from your example. This query will return all distinct values of word field in the collection.
Also, as #JohnnyHK mentioned in comment, you can use linq approach, since it is supported by C# driver:
var distinctWords = collection.AsQueryable<Element>().Select(e => e.Word).Distinct();
this work´s for me
Collection.Distinct<string>("ColumnNameForDistinct", FilterDefinition<T>.Empty).ToListAsync()
My guess would be to make "word" an index on this db.
Then using some linq to query it in a simple expression:
var res = col.Query().Select(e => e.word).Distinct();
This would result in reading all words from the index.
The MongoCollection.Distinct Method (String) V2.0 is Legacy
for new version API like 2.4 use:
FieldDefinition<yueyun.land,string> field = "FirstName";
var bx = _yueyunlands.Distinct<string>(field, Builders<yueyun.land>.Filter.Empty).ToList();
If you want to filter first and get distinct afterwards and also do all of these at MongoDB side, you can use the following example.
In this example I applied a filter, got distinct values and finally got count:
var filter = Builders<Logs>.Filter.Ne(x => x.Id, null);
var count = collection.Distinct(x => x.Id, filter).ToList().Count();
MongoDB doesn't have a built in operator to split a string of words from a query as there's not a way to split a string, then run a "distinct" operation on it.
One option would be to create a MapReduce and do the split in the MapReduce code and count each word. You can't do this with just C# code.
A second, and possibly simpler option would be to pre-split the field into words so that you could use one of the distinct operators:
{ "word": [ "some", "text"] }
Then:
dbCollection.Distinct("word");
This would of course work if you just want to treat the entire string as a "word" rather than each word separately
.
MapReduce's aren't real-time ... the pseudo-code would be:
map = function() {
var splits = this.word.split(' ');
for(var i = 0, l = splits.length; i < l; i++) {
emit(splits[i], 1);
}
}
reduce = function(word, vals) {
var count = 0;
for(var i=0, l=vals.length; i < l; i++) {
count += vals[i];
}
return count;
}
When you run the MapReduce, it would be a collection of the number of occurrences of each word.
I have a function like so
public List<Entry> GetEntriesForSlider(int first, int max, List<string> NameLetters)
{
//Some code
}
Inside this code, I want to search along a database, to return every result that has the firstname starting with a letter within the NameLetters.
So if I pass in the array NameLetters = ["a","b","c"]
Then it will return results such as
Amy
Bert
Aaron
Chris
It should be noted that I am ideally looking to use some sort of linq statement such as...
entries.Where(x => x.FirstName.StartsWith(partofArray));
If at all possible.
EDIT : I previously had the following :
var entries = _repository.All<Entry>().Skip(first).Take(max);
if (NameLetters != null && NameLetters.Count > 0)
entries = entries.Where(x => NameLetters.Contains(x.FirstName[0].ToString()));
But what I found was, it enumerated the query (I think) before running the where statement. Possibly because trying to access the first letter of firstname (Or the ToString).
If you're just looking to match the first letter try:
entries.Where(x => partofArray.Contains(x.FirstName[0]));
If you need to worry about null or empty strings a safer version would be:
entries.Where(x =>
!string.IsNullOrEmpty(x.FirstName) &&
partofArray.Contains(x.FirstName[0])
);
If you want to use variable-length strings try:
entries.Where(x =>
partofArray.Any( p=> x.FirstName.StartsWith(p))
);
Perhaps you should take a look at predicate builder.
Then something like
IQueryable<Entry> GetThingsThatBeginWithA(String[] prefixes)
{
var predicate = PredicateBuilder.False<Entry>();
foreach (String prefix in prefixes)
predicate = predicate.Or(p => p.FirstName.StartsWith(prefix));
return database.Entries.Where(predicate);
}
This query should correctly compile to a single query to the SQL store.
Update: It can also be done without predicate builder, but I find that working with expression trees without it is really tedious.
I am looking for a way to get the index of all elements in a list from a keyword search within the list. So for example my list has:
Hello World
Programming Rocks
Hello
Hello World
I love C#
Hello
Now from this list of strings, i want to get all the indices of elements that say Hello World. I have tried the following but it only returns the first index it finds that has my search criteria:
for (int i = 0; i< searchInList.Count; i++)
foundHelloWorld[i] = searchInList.IndexOf("Hello World");
Anyone know of a way to do this?
Thanks
searchInList.Select((value, index) => new {value, index})
.Where(a => string.Equals(a.value, "Hello World"))
.Select(a => a.index)
If you're trying to search for more than just "Hello World", you could do
searchInList.Select((value, index) => new {value, index})
.Where(a => stringsToSearchFor.Any(s => string.Equals(a.value, s)))
.Select(a => a.index)
Since you know you're looking for ALL occurrences and therefore you must traverse the entire list anyway, you'll gain a lot of readability over using IndexOf by simply examining each element yourself:
var i=0;
foreach(var value in searchInList)
{
if(value == "Hello World")
foundHelloWorld.Add(i); //foundHelloWorld must be an IList
i++;
}
You can also use an overload of the Linq Select method that incorporate's the element's index in the source collection; this should be highly readable (and thus maintainable) to Linq-experienced programmers:
foundHelloWorld = searchInList
.Select((v,i)=>new {Index = i, Value = v})
.Where(x=>x.Value == "Hello World")
.Select(x=>x.Index)
.ToList();
The above code takes the list and transforms the string into a simple anonymous type incorporating each item's place in the original list. Then, it filters down to only matching elements, and then it projects out the Index (which didn't change through the filtering) into a new List object. However, all this transformation will make this solution perform slower, because this statement will traverse the entire list multiple times.
A little ugly but will work:
var searchInList = new List<string>();
//Populate your list
string stringToLookUp= "Hello world";
var foundHelloWorldIndexes = new List<int>();
for (int i = 0; i < searchInList.Count; i++)
if (searchInList[i].Equals(stringToLookUp))
foundHelloWorldIndexes.Add(i);
The FindAll method for lists is here. The list extension method Where is here.
Both of these will do pretty much exactly what you want and they are pretty easy to use. There are abundant examples of each on the internet but if you need help using them just let me know.