I have a string array and a for loop to add items to list.
Here is my code:
//customers table :
// Firstname, Lastname, Age, BrowserName, Date
string[] browsers = {"chrome", "Firefox", "Edge"}
var users = db.customers.ToList();
list<string> names = new List<string>();
for(var i = 0; i < browsers.lenght; i++) {
names.Add(users.where(x => x.BrowserName == browsers[i]).FirstName);
}
Is there any way to use Linq method or something else instead of for ?
Instead of
for(var i = 0; i < browsers.lenght; i++) {
names.Add(users.where(x => x.BrowserName == browsers[i]).FirstName);
}
Use this
names = users.Where(x => browsers.Contains(x.BrowserName)).Select(y => y.FirstName).ToList();
You can do it by foreach like the following:
browsers.ToList().Foreach(x => users.where(y => y.BrowserName == x).ToList().Foreach(z => names.Add(z.FirstName)))
Related
I have List with some names:
List<string> promoNames = new List<string>();
I need to get values related to these names from DB with EF.
I tried to do it with for loop, but I could not. Like that:
PromoCode promos = null;
for (var i = 0; i < _promoNames.Count; i++)
{
promos = await _mainDbContext.PromoCode.Where(prc => prc.Code == _promoNames[i]).SingleAsync();
_promoJsonParams.Add(promos.JsonParams);
}
How I can do it?
Thanks!
You don't need loop. Just use Contains like this:
_mainDbContext.PromoCode.Where(prc => _promoNames.Contains(prc.Code)).Select (t=>t.JsonParams).ToList()
using Contains or IndexOf
promos = await _mainDbContext.PromoCode.Where(prc => _promoNames.Contains(prc.Code)).ToListAsync();
or
promos = await _mainDbContext.PromoCode.Where(prc => _promoNames.IndexOf(prc.Code) >= 0).ToListAsync();
I want to perform a query on a list of words.
string[] words = {"word1", "word2", "word3"}
In my database I have a list of tracks with artist and titles. I want to concatenate all the queries with OrWhere conditions.
This is what I have for the moment :
var word;
var queryTracks;
for (int i = 0; i < words.Length; i++)
{
word = words[i];
queryTracks = db.Tracks.Where(c => c.Title.Contains(word) || c.Artist.Contains(word));
}
// all found, put all that into a list
var filteredTracks = queryTracks.ToList();
I already tried with the .Concat() method like this :
var word = words[0];
var queryTracks = db.Tracks.Where(c => c.Title.Contains(word) || c.Artist.Contains(word));;
for (int i = 1; i < words.Length; i++)
{
word = words[i];
queryTracks = queryTracks.Concat(db.Tracks.Where(c => c.Title.Contains(word) || c.Artist.Contains(word)));
}
But it didn't work... I just want to have all the tracks with word1, word2 and word3. Can I make a big query and then put it into a list or do I have to make list1, list2 and list3 and concatenate them ?
You can use a single query
string[] words = {"word1", "word2", "word3"}
IEnumerable<Track> tracks = db.Tracks.Where(c => words.Contains(c.Title) || words.Contains(c.Artist));
I have a class RuleDetail:
public class RuleDetail
{
public int RuleDetailId;
public int StartYear;
}
I have a List of objects of type RuleDetail:
RuleDetailId=1, StartYear=0
RuleDetailId=2, StartYear=2
RuleDetailId=3, StartYear=4
RuleDetailId=4, StartYear=10
RuleDetailId=5, StartYear=13
RuleDetailId=6, StartYear=18
I will be given a number say x (x always >= 0); for that I need to find the RuleDetail object in the above List which matches these conditions:
Get the RuleDetail object where x equals to StartYear OR
Get the RuleDetail object of the max(StartYear) when StartYear < x
Assuming I have these variables
RuleDetail[] ruleDetails = null;
int x = -1;
// ruleDetails populated
// x populated
This is the code I have come up with:
bool found = false;
RuleDetail ruleDetail = null;
RuleDetail oldRuleDetail = null;
for (int i=0; i<ruleDetails.Length; i++)
{
if (ruleDetails[i].StartYear == x)
{
found = true;
ruleDetail = ruleDetails[i];
break;
}
else if (ruleDetails[i].StartYear > x)
{
found = true;
ruleDetail = oldRuleDetail;
break;
}
oldRuleDetail = ruleDetails[i];
}
if (!found)
{
ruleDetail = oldRuleDetail;
}
return ruleDetail;
The code is working ok. But how can I do this in LINQ?
Thanks
var output = ruleDetails.OrderBy(rule => rule.StartYear).Where(rule => rule.StartYear <= x).Last()
If the list is already in StartYear order then ....
var output = ruleDetails.Where(rule => rule.StartYear <= x).Last()
You can use
ruleDetails.FirstOrDefault(rd => rd.StartYear == x)
?? ruleDetails.Where(rd => rd.StartYear < x).OrderByDescending(rd => rd.StartYear).First();
which is a clear separation of your two requirements, but it is actually more concise to use
ruleDetails.Where(rd => rd.StartYear <= x).OrderByDescending(rd => rd.StartYear).First()
var res1 = (from a in ruleDetails where a.StartYear == x select a).First();
var res2 = (from a in ruleDetails orderby a.StartYear where a.StartYear < x select a).Last();
Here's a real simple LINQ snippet to accomplish what you're trying to do here. I'm writing this with the assumption that your list is ordered as that's what your current code suggests.
We'll first filter the list down to entries that are either the target year or less than it, then take the highest remaining element.
var filteredList = ruledetails.Where(r => r.StartYear <= targetYear);
return filteredList.Last;
It's kinda gross, but maybe something like:
var result = ruleDetails
.OrderBy(r => r.StartYear)
.FirstOrDefault(r => r.StartYear == x || r.StartYear == ruleDetails.Select(y => y.StartYear).Max());
(A) if the list is is initially sorted by StartYear, then
var result = ruleDetails.LastOrDefault(r => r.StartYear <= startYear);
(B) if the list is not sorted, then
var result = ruleDetails.Where(r => r.StartYear <= startYear)
.Aggregate((RuleDetail)null, (a, b) => a == null || a.StartYear < b.StartYear ? b : a);
I have a list which contains the name of suppliers. Say
SuppId Supplier Name
----------------------------------
1 Aardema & Whitelaw
2 Aafedt Forde Gray
3 Whitelaw & Sears-Ewald
using following LINQ query
supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey));
I can return records correctly in the following conditions,
1) If i am using search string as "Whitelaw & Sears-Ewald" it will return 3rd record.
2) If i am using "Whitelaw" or "Sears-Ewald" it will return 3rd record.
But how can i return 3rd record if i am giving search string as "Whitelaw Sears-Ewald". It always returns 0 records.
Can i use ALL to get this result, but i dont know how to use it for this particular need.
What I usually do in this situation is split the words into a collection, then perform the following:
var searchopts = SearchKey.Split(' ').ToList();
supplierListQuery = supplierListQuery
.Where(x => searchopts.Any(y=> x.SupplierName.Contains(y)));
This works for me:
IEnumerable<string> keyWords = SearchKey.Split('');
supplierListQuery = supplierListQuery
.AsParallel()
.Where
(
x => keyWords.All
(
keyword => x.SupplierName.ContainsIgnoreCase(keyword)
)
);
Thank you all for your quick responses. But the one which worked or a easy fix to handle this was timothyclifford's note on this. Like he said i alterd my answer to this
string[] filters = SearchKey.ToLower().Split(new[] { ' ' });
objSuppliersList = (from x in objSuppliersList
where filters.All(f => x.SupplierName.ToLower().Contains(f))
select x).ToList();
Now it returns the result for all my serach conditions.
Because "Whitelaw" appears in both you will get both records. Otherwise there is no dynamic way to determine you only want the last one. If you know you only have these 3 then append .Last() to get the final record.
supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey.Split(' ')[0]));
You need to use some sort of string comparer to create your own simple Search Engine and then you can find strings that are most likely to be included in your result :
public static class SearchEngine
{
public static double CompareStrings(string val1, string val2)
{
if ((val1.Length == 0) || (val2.Length == 0)) return 0;
if (val1 == val2) return 100;
double maxLength = Math.Max(val1.Length, val2.Length);
double minLength = Math.Min(val1.Length, val2.Length);
int charIndex = 0;
for (int i = 0; i < minLength; i++) { if (val1.Contains(val2[i])) charIndex++; }
return Math.Round(charIndex / maxLength * 100);
}
public static List<string> Search(this string[] values, string searchKey, double threshold)
{
List<string> result = new List<string>();
for (int i = 0; i < values.Length; i++) if (CompareStrings(values[i], searchKey) > threshold) result.Add(values[i]);
return result;
}
}
Example of usage :
string[] array = { "Aardema & Whitelaw", "Aafedt Forde Gray", "Whitelaw & Sears-Ewald" };
var result = array.Search("WhitelawSears-Ewald", 80);
// Results that matches this string with 80% or more
foreach (var item in result)
{
Console.WriteLine(item);
}
Output: Whitelaw & Sears-Ewald
If you want an easy (not very handy) solution,
var result = supplierListQuery
.Select(x => normalize(x.SupplierName))
.Where(x => x.Contains(normalize(SearchKey)));
string normalize(string inputStr)
{
string retVal = inputStr.Replace("&", "");
while (retVal.IndexOf(" ") >= 0)
{
retVal = retVal.Replace(" ", " ");
}
return retVal;
}
I have this:
List<string> list = new List<string>();
list.Add("a-b-c>d");
list.Add("b>c");
list.Add("f>e");
list.Add("f>e-h");
list.Add("a-d>c-b");
I want to delete duplicates. In this case duplicates are "a-b-c>d" and "a-d>c-b". Both have same chars but in diferente order.
I tried with:
list.Distinct().ToList();
But didn't work!
It looks like you want:
var distinct = list
.Select((str, idx) => new { Str = str, Idx = idx })
.GroupBy(pair => new HashSet<char>(pair.Str), HashSet<char>.CreateSetComparer())
.Select(grp => grp.OrderBy(p => p.Idx).First())
.ToList();
This will keep the first element and remove any later strings in the sequence which contains the same characters.
You can also use Aggregate to track the character sets you've already seen:
var distinct = list
.Aggregate(new Dictionary<HashSet<char>, string>(HashSet<char>.CreateSetComparer()), (dict, str) =>
{
var set = new HashSet<char>(str);
if (!dict.ContainsKey(set))
dict.Add(set, str);
return dict;
})
.Values
.ToList();
You'll have to define a custom IEqualityComparer that allows the system to understand when you consider two strings to be "equal". For example:
List<string> list = new List<string>();
list.Add("a-b-c>d");
list.Add("b>c-d-f");
list.Add("c-d-f>e");
list.Add("a-d>c-b");
var distinctItems = list.Distinct(new KeyFuncEqualityComparer<string>(
s => new String(s.AsEnumerable().OrderBy(c => c).ToArray())));
Result:
a-b-c>d
b>c-d-f
c-d-f>e
... using this generic IEqualityComparer implementation:
public class KeyFuncEqualityComparer<T> :IEqualityComparer<T>
{
private readonly Func<T, object> _getKey;
public KeyFuncEqualityComparer(Func<T, object> getKey)
{
_getKey = getKey;
}
public bool Equals(T x, T y)
{
return _getKey(x).Equals(_getKey(y));
}
public int GetHashCode(T obj)
{
return _getKey(obj).GetHashCode();
}
}
untested
list<int> dups = new list<int>();
for(int i = 0; i < list.count - 1; i++)
{
for(int j = 1; j < list.count; j++)
{
int len = list[j].length;
bool b = false;
for(int k = 0; k < list[i].length; k++)
{
if(k < len && (list[i][k] != list[j][k]))
{
b = false;
break;
}
b = true;
}
if(b == true)
{
dups.add(i);
}
}
}
then remove all dups from list