EF get record for each item - c#

Hello guys this is what I have so far.
public List<Word> GetWords(string[] words)
{
return DbContext.Words.Where(w => words.Contains(w.Value.ToLower());
}
The problem is that when I pass for example List containing two same words I am getting one entity. What can be an effective way of getting entity for each word even if it's duplicate?

Given this simplified test case, where sourceData represents your DbContext.Words:
var sourceData = new string[]
{
"a",
"b",
"c"
};
var wordsToFind = new string[]
{
"a",
"a"
};
A silly way would be to execute the query for each requested input:
var foundWords = wordsToFind.Select(w =>
sourceData.Where(s => s.Contains(w.ToLower()))).ToList();
Or you could execute the query once, then duplicate the results per input by executing the query in-memory again:
var foundWords = sourceData.Where(w =>
wordsToFind.Contains(w.ToLower())).ToList();
var result = wordsToFind.SelectMany(w =>
foundWords.Where(f =>
f.Contains(w.ToLower()))).ToList();

Not sure if you want substring or equal words, following query returns multiple records but checking for the same word not substring.
public List<Word> GetWords(string[] words)
{
var results = from word in DbContext.Words.ToArray()
join str in words on word.ToLower() equals str
select word;
return results.ToList();
}
EDIT: First get the filtered records from the database and then join it with the array again to get multiple records. Same thing checking twice. Stored procedure would be more efficient for a huge collection.
public List<Word> GetWords(string[] words)
{
var results = from word in DbContext.Words
.Where(w => words.Contains(w.Value.ToLower())
.ToArray()
join str in words on str.Contains(word.ToLower())
equals true
select word;
return results.ToList();
}

Related

How to include generic behaviour in contains string [duplicate]

I've been trying to solve this problem all day, and haven't found a solution that truly works. When I search for some data, I want to filter out the data based on multiple words.
My input value is split up by using the standard .Split-function.
string[] searchstrings = MessageResult.Split(' ');
I've made a query (which obviously doesn't work properly) that tries to filter out all the entries that matches every string in searchstrings.
var suggestions = (from a in query
from w in searchstrings
where a.Message.ToLower().Contains(w.ToLower())
select a).Distinct();
query is my variable which has all the data. How can I make this query to actually only match out entries that includes every string in searchstrings?
I think code below should solve your problem. It checks if all words in searchstring are in a query (a).
var suggestions = (from a in query
where searchstrings.All(word => a.ToLower().Contains(word.ToLower()))
select a);
var query = new string[]
{
"abc foo bar xyz john doe",
"abc foo bar xyz doe",
"hello world",
"abc foo bar john doe",
};
var searchstrings = new string[]
{
"abc",
"foo",
"john",
"xyz",
};
searchstrings = searchstrings.Select(x => x.ToLower()).ToArray();
var results = query.Select(x => x.ToLower())
.Where(x => searchstrings.All(y => x.Contains(y)));
Note:
ToLower() is performed outside the Where clause, to save a lot of calls to that method.

Filter a collection column by multiple values

I have a collection and I would like to filter it with one of the column contains multiple values. The filter values are dynamically generated and I dont know how many I will get.
I tried the following without success:
var input = #"was.Name.Contains(""Test"") || was.Name.Contains(""Test2"")";
var test = collection.Where(was => input)).ToList();
Assuming you receive the filter values as a CSV string:
var csvFilters = "Test1, Test2";
// split by ',', remove empty entries,
// trim each filter and store the result in a list
var filters = csvFilters.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
// return items in collection whose Name property
// is equal to any of the items in filters
var result = collection.Where(x => filters.Contains(x.Name)).ToList();
This should translate to the following SQL:
SELECT * FROM collection c
WHERE c.Name IN ('Test1', 'Test2')
I guess you want to use LINQ. The question is, how the "filter" values are kept? I'll answer in the way I understand your question.
If input is supposed to be a condition then I'd suggest using Func<Object,bool>. This means, the input would be the condition you're looking for, and if found, it would return true.
Here is a simple example:
IEnumerable <T> FindElements (Func<Object, bool> condition, IEnumerable<T> inputList)
{
List<T> outputList = new List<T>();
foreach(var element in inputList)
{
if(condition != null && condition(element))
outputList.Add(element);
}
return outputList;
}
Then, if you call the function given exemplary parameters:
string input[] = {"Test1","Test2"};
foreach(string s in input)
{
targetList = FindElements(element=>((cast)element).Name.Contains(s), collection);
}
You should get all elements in collection which name has Test1 or Test2. Cast is of course name of the class which element instantiates.

Contains without order

I want to search a list of strings using a set of characters and want to find matches regardless of order. For example if my list contains
List<string> testList = new List<string>() { "can", "rock", "bird" };
I want to be able to search using "irb" and have it return bird. I have to do this many times so I am looking for the most efficient way of doing it.
var query = "irb";
List<string> testList = new List<string>() { "can", "rock", "bird" };
var result = testList.Where(i => query.All(q => i.Contains(q)));
For each item in the testList test to see if it contains all the letters in query
For your scenario, you need to check each character of word in another list of word.
For that, you can do like this :
// Checks whether all character in word is present in another word
Func<string, string, bool> isContain = (s1, s2) =>
{
int matchingLength = 0;
foreach (var c2 in s2.ToCharArray())
{
foreach (var c1 in s1.ToCharArray())
{
if (c1 == c2)
++matchingLength;
}
}
// if matched length is equal to word length given, it would be assumed as matched
return s2.Length == matchingLength;
};
List<string> testList = new List<string>() { "can", "rock", "bird" };
string name = "irb";
var fileredList = testList.Where(x => isContain(x, name));
If you don't care about matching duplicates than checking if all characters in a sequence you are searching for are contained in the word would do for predicate:
"irb".Except("bird").Count() == 0
And whole condition:
List<string> testList = new List<string>() { "can", "rock", "bird" };
var search = "irb";
var matches = testList.Where(word => !search.Except(word).Any());
Notes:
you need to normalize all words to lowercase if you need mixed case letters to match.
if performance of searching for different values is critical - convert search string to HashSet first and do except manually.
if you need to match different values against same list many times - convert list of strings to list of HashSet and use search.All(c => wordAsHashSet.Contains(c)) as condition.
You can use linq to achieve this
List<string> testList = new List<string>() { "can", "rock", "bird" };
var lst = testList.Where(x => x.ToUpperInvariant().Contains("IRD")).ToList();
Make sure you also compare the cases using ToUpper and the string you want to compare also make it UpperCase

search the database for the words within a string

Imagine that a user entered a sentence and I need to search for the subjects that consist of words within the entered sentence. These are the code that I thought they could solve the case.
var result = from x in dataBase.tableName
select x;
string[] words = enteredString.Split();
foreach(string word in words)
{
result = result.Where(x => x.subject.Contains(word));
}
it shows only the search result with the last word in sentence, but I thought the result must be narrowed down each time a word is used in the where line.
Try this:
foreach(string word in words)
{
var temp = word;
result = result.Where(x => x.subject.Contains(temp));
}
This is called (by ReSharper at least) "access to modified closure" - lambda expressions don't capture the value, they capture the entire variable. And the value of the variable word is changing with each iteration of the loop. So, since the Where() method is lazy-evaluated, by the time this sequence is consumed, the value of word is the last one in the sequence.
I hade some success by inverting the logic like this:
string[] words = enteredString.Split();
var results = from x in database.TableName
where words.Any(w => x.subject.Contains(w))
select x;
-- Edit
A more generic approach, for this kind of queries, would be:
class SearchQuery
{
public ICollection<string> Include { get; private set; }
public ICollection<string> Exclude { get; private set; }
}
[...]
SearchQuery query = new SearchQuery
{
Include = { "Foo" }, Exclude = { "Bar" }
}
var results = from x in database.Table
where query.Include.All(i => x.Subject.Contains(i)) &&
query.Exclude.All(i => !x.Subject.Contains(i))
select x;
This assumes that all words in query.Include must occur in Subject, if you want to find any subjects that have at least one of the words query.Include.All should be query.Include.Any
I've tested this with Entity Framework 4. Which will create a SQL query that applies all criteria in the database rather than in memory.
Here you go:
var result = from x in dataBase.tableName
select x;
string[] words = enteredString.Split();
result.Where(r => words.Any(w => r.Subject.Contains(w));
it can't do the thing - since with every word you are overwriting the previous result - you need to do something similar to:
List<object> AllResults = new List<object>();
foreach(string word in words)
{
var temp = word;
AllResults.AddRange (result.Where(x => x.subject.Contains(temp)).ToList());
}
Not sure what type your result type is hence the List<object>...

Get records which do not start with an alphabetical character in linq

I need to get a list of records that do not start with an alphabetical character, i.e. which either starts with a numerical character or any special character.
Whats the simple LINQ query to get this list?
List<string> Entries = new List<string>();
Entries.Add("foo");
Entries.Add("bar");
Entries.Add("#foo");
Entries.Add("1bar");
var NonAlphas = (from n in Entries
where !char.IsLetter(n.ToCharArray().First())
select n);
For Linq-to-sql you could hydrate your retrieval from the database by 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<string> Entries = dbContext.Entry.Where(n => n.EntryName).ToList();
var NonAlphas = Entries.Where(n => !char.IsLetter(n.First()));
Something like this?
List<string> lst = new List<string>();
lst.Add("first");
lst.Add("second");
lst.Add("third");
lst.Add("2abc");
var result = from i in lst where !char.IsLetter(i[0]) select i;
List<string> output = result.ToList();
Edit: I realized that using Regex here was overkill and my solution wasn't perfect anyway.
string[] x = new string[3];
x[0] = "avb";
x[1] = "31df";
x[2] = "%dfg";
var linq = from s in x where !char.IsLetter(s.ToString().First()) select s;
List<string> simplelist = new List<string>(linq);
/* in simple list you have only "31df" & "dfg" */
One thing to note is that you don't need to convert the string to a chararray to use linq on it.
The more consise version would be:
var list = new List<string> {"first","third","second","2abc"};
var result = list.Where(word => !char.IsLetter(word.First()));

Categories